feat(learn,app): explanations wait behind a question mark beside their title

What explains a setting or a section, rather than what is the person's
own, moves behind halkyon_ui's HelpButton, beside its title through the
new ExplainedTitle (the developer's review of 3 Nov 2026): in Learn's
settings (citations, the Pomodoro timer, grammar, the web search,
Jellyfin, the device's dictionaries, counting one's learning), the study
settings, export's progress, the Jellyfin tags and the study button's
back first; in halkyon_app, setting up a password, adding SimpleLogin,
own keys not allowed, the profile page's link back and the operator's
grant sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-03 22:24:39 +01:00
commit 830aea10fe
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 279 additions and 115 deletions

View file

@ -455,7 +455,11 @@ class _GrantSheetState() extends State<_GrantSheet> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (grantee == null) ...[
Text(l10n.assistantPeopleTitle, style: heading),
ExplainedTitle(
l10n.assistantPeopleTitle,
help: l10n.assistantGrantNote,
style: heading,
),
const SizedBox(height: 8),
if (state.accounts.isNotEmpty) ...[
Wrap(
@ -505,7 +509,14 @@ class _GrantSheetState() extends State<_GrantSheet> {
),
],
const SizedBox(height: 16),
Text(l10n.assistantGrantBudget, style: heading),
ExplainedTitle(
l10n.assistantGrantBudget,
help: [
l10n.assistantGrantLimitHelper,
if (priced) l10n.assistantGrantMoneyNote,
].join(' '),
style: heading,
),
const SizedBox(height: 8),
if (priced)
_LimitRow(
@ -520,13 +531,6 @@ class _GrantSheetState() extends State<_GrantSheet> {
suffix: null,
enabled: !state.busy,
),
Text(
[
l10n.assistantGrantLimitHelper,
if (priced) l10n.assistantGrantMoneyNote,
].join(' '),
style: muted,
),
if (grantee == null && state.replaced.isNotEmpty) ...[
const SizedBox(height: 16),
_Replaced(state.replaced, currency: setUp.currency),
@ -538,10 +542,6 @@ class _GrantSheetState() extends State<_GrantSheet> {
style: TextStyle(color: theme.colorScheme.error),
),
],
if (grantee == null) ...[
const SizedBox(height: 16),
Text(l10n.assistantGrantNote, style: muted),
],
],
),
footer: FilledButton(

View file

@ -72,7 +72,7 @@ class const _AssistantsView({
return const SizedBox.shrink();
}
final offer = state.offer;
final muted = theme.textTheme.bodyMedium?.copyWith(
final subtitle = theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
);
return Padding(
@ -141,21 +141,16 @@ class const _AssistantsView({
if (ownKept && (offer.ownKeys || state.own.isNotEmpty)) ...[
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
l10n.assistantOwnTitle,
style: theme.textTheme.titleSmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
// Why own keys go unasked, where they are not allowed,
// behind its question mark (decided 3 Nov 2026).
child: offer.ownKeys
? Text(l10n.assistantOwnTitle, style: subtitle)
: ExplainedTitle(
l10n.assistantOwnTitle,
help: l10n.assistantOwnNotAllowed(instance),
style: subtitle,
),
),
if (!offer.ownKeys)
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
l10n.assistantOwnNotAllowed(instance),
style: muted,
),
),
for (final own in state.own)
ListTile(
contentPadding: EdgeInsets.zero,

View file

@ -79,8 +79,10 @@ class const _AccountSecurity() extends StatelessWidget {
ListTile(
contentPadding: EdgeInsets.zero,
leading: const Icon(LucideIcons.keyRound),
title: Text(l10n.passwordSectionSetUp),
subtitle: Text(l10n.passwordSectionSetUpHint),
title: ExplainedTitle(
l10n.passwordSectionSetUp,
help: l10n.passwordSectionSetUpHint,
),
onTap: () => _setUp(context, security),
),
] else if (!security.finished)

View file

@ -731,9 +731,6 @@ class const _ProfilePage({required final String url}) extends StatelessWidget {
Widget build(BuildContext context) {
final l10n = HalkyonAppLocalizations.of(context);
final theme = Theme.of(context);
final muted = theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
);
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Column(
@ -751,6 +748,9 @@ class const _ProfilePage({required final String url}) extends StatelessWidget {
overflow: TextOverflow.ellipsis,
),
),
// What linking back does, behind its question mark (decided
// 3 Nov 2026).
HelpButton(l10n.publicProfileVerifyHint),
IconButton(
tooltip: l10n.publicProfilePageCopy,
icon: const Icon(LucideIcons.copy, size: 18),
@ -766,7 +766,6 @@ class const _ProfilePage({required final String url}) extends StatelessWidget {
),
],
),
Text(l10n.publicProfileVerifyHint, style: muted),
],
),
);

View file

@ -7,6 +7,7 @@ import 'package:halkyon_app/src/simplelogin/model/simplelogin_standing.dart';
import 'package:halkyon_app/src/simplelogin/widget/keys_reset.dart';
import 'package:halkyon_app/src/simplelogin/widget/simplelogin_sheet.dart';
import 'package:halkyon_app/src/simplelogin/widget/simplelogin_standing_scope.dart';
import 'package:halkyon_ui/halkyon_ui.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:material_ui/material_ui.dart';
@ -64,8 +65,10 @@ class const _SimpleLoginSection({required final double gapAbove})
ListTile(
contentPadding: EdgeInsets.zero,
leading: const Icon(LucideIcons.link),
title: Text(l10n.simpleLoginAddTitle),
subtitle: Text(l10n.simpleLoginAddHint),
title: ExplainedTitle(
l10n.simpleLoginAddTitle,
help: l10n.simpleLoginAddHint,
),
onTap: () => addSimpleLogin(context),
),
],