feat(learn): the assistant answers in the field

The field asks an assistant the instance offers as well as searching the
web (ADR 0021): who answers is picked at its start, the one asked last
first (askWith, a device setting), an assistant not agreed to yet asking
for agreement first. Asking an assistant, every part of the topic may go
along, and one chip leads them with how far it may look (D49), only what
is chosen until the person widens it to the studyset, its tree or the
library, private studysets only where switched on (D50, D8). The
conversation shows in a sheet over the screen (D47), half the height and
dragged up to the full: each question with what went along, the lookups,
the answer as Markdown once whole, follow-ups, Stop, and Try again for a
question that got no answer; a device that can't seal says it keeps
nothing (D52). The field lists the conversations kept (D48), those about
where the person is first, to open again or delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-25 20:30:17 +01:00
commit 806ffae202
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 2312 additions and 72 deletions

View file

@ -1817,6 +1817,45 @@
"assistantHintWeb": "Im Web suchen…",
"assistantSendWeb": "Suchen",
"assistantSearchFailed": "Die Suche ließ sich nicht öffnen.",
"assistantChoose": "Wer antwortet",
"assistantWebSearch": "Websuche",
"assistantChooseAgree": "Erst lesen und zustimmen",
"assistantChooseOwn": "Mit deinem Schlüssel",
"assistantHintAsk": "{name} fragen…",
"assistantSendAsk": "Fragen",
"assistantScope": "Was er nachschlagen darf",
"assistantScopeTicked": "Nur was ich auswähle",
"assistantScopeStudyset": "Dieses Lernset",
"assistantScopeTree": "Ganzer Baum",
"assistantScopeLibrary": "Bibliothek",
"assistantScopeTickedLong": "Nur was ich oben auswähle",
"assistantScopeStudysetLong": "Dieses Lernset mit seinen Themen",
"assistantScopeTreeLong": "Sein ganzer Baum, von oben",
"assistantScopeLibraryLong": "Meine ganze Bibliothek",
"assistantScopePrivate": "Private Lernsets einbeziehen",
"assistantScopeWithPrivate": "{scope}, auch private",
"assistantHistory": "Unterhaltungen",
"assistantHistoryEmpty": "Noch keine Unterhaltungen",
"assistantHistoryEmptyHint": "Frag einen Assistenten im Feld unten.",
"assistantHistoryDelete": "Unterhaltung löschen",
"assistantHistoryDeleteMessage": "„{title}“ verschwindet von allen deinen Geräten.",
"assistantHistoryDeleteConfirm": "Löschen",
"assistantGone": "Ein nicht mehr angebotener Assistent",
"assistantGoneNote": "Dieser Assistent wird dir hier nicht mehr angeboten, also lässt sich die Unterhaltung nicht fortsetzen.",
"assistantNotKept": "Nicht gespeichert: Dieses Gerät kann Unterhaltungen nicht verschlüsseln, also ist diese weg, sobald du sie schließt.",
"assistantFollowUp": "Weiter fragen…",
"assistantStop": "Anhalten",
"assistantAnswering": "Antwortet…",
"assistantStopped": "Vor der Antwort angehalten.",
"assistantStoppedEarly": "Er hat zu oft nachgeschlagen und ohne Antwort aufgehört.",
"assistantNoAnswer": "Keine Antwort.",
"assistantCopyAnswer": "Antwort kopieren",
"assistantCopied": "Antwort kopiert",
"assistantLookedUpStudysets": "{count, plural, =0{Keine Lernsets} =1{1 Lernset} other{{count} Lernsets}} aufgelistet",
"assistantLookedUpCards": "Karten nach „{query}“ durchsucht: {count, plural, =0{keine gefunden} =1{1 gefunden} other{{count} gefunden}}",
"assistantLookedUpNotes": "Notizen nach „{query}“ durchsucht: {count, plural, =0{keine gefunden} =1{1 gefunden} other{{count} gefunden}}",
"assistantLookedUpNote": "Eine Notiz gelesen",
"assistantLookedUpNoNote": "Eine Notiz gesucht, keine gefunden",
"settingsSearchEngine": "Suchmaschine",
"searchEngineDevice": "Suche des Geräts",
"searchEngineCustom": "Eigene Adresse",

View file

@ -2131,6 +2131,53 @@
"assistantHintWeb": "Search the web…",
"assistantSendWeb": "Search",
"assistantSearchFailed": "The search couldn't be opened.",
"assistantChoose": "Who answers",
"assistantWebSearch": "Web search",
"assistantChooseAgree": "Read and agree first",
"@assistantChooseAgree": {"description": "Under an assistant in the field's menu that the person hasn't agreed to yet."},
"assistantChooseOwn": "With your key",
"assistantHintAsk": "Ask {name}…",
"@assistantHintAsk": {"placeholders": {"name": {"type": "String"}}},
"assistantSendAsk": "Ask",
"assistantScope": "What it may look up",
"@assistantScope": {"description": "Tooltip of the scope chip: how far an assistant may look beyond the parts chosen."},
"assistantScopeTicked": "Only what I choose",
"assistantScopeStudyset": "This studyset",
"assistantScopeTree": "Its tree",
"assistantScopeLibrary": "Library",
"assistantScopeTickedLong": "Only what I choose above",
"assistantScopeStudysetLong": "This studyset with its topics",
"assistantScopeTreeLong": "Its whole tree, from the top",
"assistantScopeLibraryLong": "My whole library",
"assistantScopePrivate": "Include private studysets",
"assistantScopeWithPrivate": "{scope}, private ones too",
"@assistantScopeWithPrivate": {"placeholders": {"scope": {"type": "String"}}},
"assistantHistory": "Conversations",
"assistantHistoryEmpty": "No conversations yet",
"assistantHistoryEmptyHint": "Ask an assistant in the field below.",
"assistantHistoryDelete": "Delete conversation",
"assistantHistoryDeleteMessage": "“{title}” goes from all your devices.",
"@assistantHistoryDeleteMessage": {"placeholders": {"title": {"type": "String"}}},
"assistantHistoryDeleteConfirm": "Delete",
"assistantGone": "An assistant no longer offered",
"assistantGoneNote": "This assistant is no longer offered to you here, so the conversation can't go on.",
"assistantNotKept": "Not kept: this device can't encrypt conversations, so this one is gone once you close it.",
"assistantFollowUp": "Ask more…",
"assistantStop": "Stop",
"assistantAnswering": "Answering…",
"assistantStopped": "Stopped before it answered.",
"assistantStoppedEarly": "It looked things up too often and stopped without an answer.",
"assistantNoAnswer": "No answer.",
"assistantCopyAnswer": "Copy the answer",
"assistantCopied": "Answer copied",
"assistantLookedUpStudysets": "Listed {count, plural, =0{no studysets} =1{1 studyset} other{{count} studysets}}",
"@assistantLookedUpStudysets": {"placeholders": {"count": {"type": "int"}}},
"assistantLookedUpCards": "Looked for “{query}” in cards: {count, plural, =0{none found} =1{1 found} other{{count} found}}",
"@assistantLookedUpCards": {"placeholders": {"query": {"type": "String"}, "count": {"type": "int"}}},
"assistantLookedUpNotes": "Looked for “{query}” in notes: {count, plural, =0{none found} =1{1 found} other{{count} found}}",
"@assistantLookedUpNotes": {"placeholders": {"query": {"type": "String"}, "count": {"type": "int"}}},
"assistantLookedUpNote": "Read a note",
"assistantLookedUpNoNote": "Looked for a note, found none",
"settingsSearchEngine": "Search engine",
"searchEngineDevice": "The device's search",
"searchEngineCustom": "Your own address",

View file

@ -10719,6 +10719,240 @@ abstract class AppLocalizations {
/// **'The search couldn\'t be opened.'**
String get assistantSearchFailed;
/// No description provided for @assistantChoose.
///
/// In en, this message translates to:
/// **'Who answers'**
String get assistantChoose;
/// No description provided for @assistantWebSearch.
///
/// In en, this message translates to:
/// **'Web search'**
String get assistantWebSearch;
/// Under an assistant in the field's menu that the person hasn't agreed to yet.
///
/// In en, this message translates to:
/// **'Read and agree first'**
String get assistantChooseAgree;
/// No description provided for @assistantChooseOwn.
///
/// In en, this message translates to:
/// **'With your key'**
String get assistantChooseOwn;
/// No description provided for @assistantHintAsk.
///
/// In en, this message translates to:
/// **'Ask {name}…'**
String assistantHintAsk(String name);
/// No description provided for @assistantSendAsk.
///
/// In en, this message translates to:
/// **'Ask'**
String get assistantSendAsk;
/// Tooltip of the scope chip: how far an assistant may look beyond the parts chosen.
///
/// In en, this message translates to:
/// **'What it may look up'**
String get assistantScope;
/// No description provided for @assistantScopeTicked.
///
/// In en, this message translates to:
/// **'Only what I choose'**
String get assistantScopeTicked;
/// No description provided for @assistantScopeStudyset.
///
/// In en, this message translates to:
/// **'This studyset'**
String get assistantScopeStudyset;
/// No description provided for @assistantScopeTree.
///
/// In en, this message translates to:
/// **'Its tree'**
String get assistantScopeTree;
/// No description provided for @assistantScopeLibrary.
///
/// In en, this message translates to:
/// **'Library'**
String get assistantScopeLibrary;
/// No description provided for @assistantScopeTickedLong.
///
/// In en, this message translates to:
/// **'Only what I choose above'**
String get assistantScopeTickedLong;
/// No description provided for @assistantScopeStudysetLong.
///
/// In en, this message translates to:
/// **'This studyset with its topics'**
String get assistantScopeStudysetLong;
/// No description provided for @assistantScopeTreeLong.
///
/// In en, this message translates to:
/// **'Its whole tree, from the top'**
String get assistantScopeTreeLong;
/// No description provided for @assistantScopeLibraryLong.
///
/// In en, this message translates to:
/// **'My whole library'**
String get assistantScopeLibraryLong;
/// No description provided for @assistantScopePrivate.
///
/// In en, this message translates to:
/// **'Include private studysets'**
String get assistantScopePrivate;
/// No description provided for @assistantScopeWithPrivate.
///
/// In en, this message translates to:
/// **'{scope}, private ones too'**
String assistantScopeWithPrivate(String scope);
/// No description provided for @assistantHistory.
///
/// In en, this message translates to:
/// **'Conversations'**
String get assistantHistory;
/// No description provided for @assistantHistoryEmpty.
///
/// In en, this message translates to:
/// **'No conversations yet'**
String get assistantHistoryEmpty;
/// No description provided for @assistantHistoryEmptyHint.
///
/// In en, this message translates to:
/// **'Ask an assistant in the field below.'**
String get assistantHistoryEmptyHint;
/// No description provided for @assistantHistoryDelete.
///
/// In en, this message translates to:
/// **'Delete conversation'**
String get assistantHistoryDelete;
/// No description provided for @assistantHistoryDeleteMessage.
///
/// In en, this message translates to:
/// **'“{title}” goes from all your devices.'**
String assistantHistoryDeleteMessage(String title);
/// No description provided for @assistantHistoryDeleteConfirm.
///
/// In en, this message translates to:
/// **'Delete'**
String get assistantHistoryDeleteConfirm;
/// No description provided for @assistantGone.
///
/// In en, this message translates to:
/// **'An assistant no longer offered'**
String get assistantGone;
/// No description provided for @assistantGoneNote.
///
/// In en, this message translates to:
/// **'This assistant is no longer offered to you here, so the conversation can\'t go on.'**
String get assistantGoneNote;
/// No description provided for @assistantNotKept.
///
/// In en, this message translates to:
/// **'Not kept: this device can\'t encrypt conversations, so this one is gone once you close it.'**
String get assistantNotKept;
/// No description provided for @assistantFollowUp.
///
/// In en, this message translates to:
/// **'Ask more…'**
String get assistantFollowUp;
/// No description provided for @assistantStop.
///
/// In en, this message translates to:
/// **'Stop'**
String get assistantStop;
/// No description provided for @assistantAnswering.
///
/// In en, this message translates to:
/// **'Answering…'**
String get assistantAnswering;
/// No description provided for @assistantStopped.
///
/// In en, this message translates to:
/// **'Stopped before it answered.'**
String get assistantStopped;
/// No description provided for @assistantStoppedEarly.
///
/// In en, this message translates to:
/// **'It looked things up too often and stopped without an answer.'**
String get assistantStoppedEarly;
/// No description provided for @assistantNoAnswer.
///
/// In en, this message translates to:
/// **'No answer.'**
String get assistantNoAnswer;
/// No description provided for @assistantCopyAnswer.
///
/// In en, this message translates to:
/// **'Copy the answer'**
String get assistantCopyAnswer;
/// No description provided for @assistantCopied.
///
/// In en, this message translates to:
/// **'Answer copied'**
String get assistantCopied;
/// No description provided for @assistantLookedUpStudysets.
///
/// In en, this message translates to:
/// **'Listed {count, plural, =0{no studysets} =1{1 studyset} other{{count} studysets}}'**
String assistantLookedUpStudysets(int count);
/// No description provided for @assistantLookedUpCards.
///
/// In en, this message translates to:
/// **'Looked for “{query}” in cards: {count, plural, =0{none found} =1{1 found} other{{count} found}}'**
String assistantLookedUpCards(String query, int count);
/// No description provided for @assistantLookedUpNotes.
///
/// In en, this message translates to:
/// **'Looked for “{query}” in notes: {count, plural, =0{none found} =1{1 found} other{{count} found}}'**
String assistantLookedUpNotes(String query, int count);
/// No description provided for @assistantLookedUpNote.
///
/// In en, this message translates to:
/// **'Read a note'**
String get assistantLookedUpNote;
/// No description provided for @assistantLookedUpNoNote.
///
/// In en, this message translates to:
/// **'Looked for a note, found none'**
String get assistantLookedUpNoNote;
/// No description provided for @settingsSearchEngine.
///
/// In en, this message translates to:

View file

@ -6716,6 +6716,160 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get assistantSearchFailed => 'Die Suche ließ sich nicht öffnen.';
@override
String get assistantChoose => 'Wer antwortet';
@override
String get assistantWebSearch => 'Websuche';
@override
String get assistantChooseAgree => 'Erst lesen und zustimmen';
@override
String get assistantChooseOwn => 'Mit deinem Schlüssel';
@override
String assistantHintAsk(String name) {
return '$name fragen…';
}
@override
String get assistantSendAsk => 'Fragen';
@override
String get assistantScope => 'Was er nachschlagen darf';
@override
String get assistantScopeTicked => 'Nur was ich auswähle';
@override
String get assistantScopeStudyset => 'Dieses Lernset';
@override
String get assistantScopeTree => 'Ganzer Baum';
@override
String get assistantScopeLibrary => 'Bibliothek';
@override
String get assistantScopeTickedLong => 'Nur was ich oben auswähle';
@override
String get assistantScopeStudysetLong => 'Dieses Lernset mit seinen Themen';
@override
String get assistantScopeTreeLong => 'Sein ganzer Baum, von oben';
@override
String get assistantScopeLibraryLong => 'Meine ganze Bibliothek';
@override
String get assistantScopePrivate => 'Private Lernsets einbeziehen';
@override
String assistantScopeWithPrivate(String scope) {
return '$scope, auch private';
}
@override
String get assistantHistory => 'Unterhaltungen';
@override
String get assistantHistoryEmpty => 'Noch keine Unterhaltungen';
@override
String get assistantHistoryEmptyHint =>
'Frag einen Assistenten im Feld unten.';
@override
String get assistantHistoryDelete => 'Unterhaltung löschen';
@override
String assistantHistoryDeleteMessage(String title) {
return '„$title“ verschwindet von allen deinen Geräten.';
}
@override
String get assistantHistoryDeleteConfirm => 'Löschen';
@override
String get assistantGone => 'Ein nicht mehr angebotener Assistent';
@override
String get assistantGoneNote =>
'Dieser Assistent wird dir hier nicht mehr angeboten, also lässt sich die Unterhaltung nicht fortsetzen.';
@override
String get assistantNotKept =>
'Nicht gespeichert: Dieses Gerät kann Unterhaltungen nicht verschlüsseln, also ist diese weg, sobald du sie schließt.';
@override
String get assistantFollowUp => 'Weiter fragen…';
@override
String get assistantStop => 'Anhalten';
@override
String get assistantAnswering => 'Antwortet…';
@override
String get assistantStopped => 'Vor der Antwort angehalten.';
@override
String get assistantStoppedEarly =>
'Er hat zu oft nachgeschlagen und ohne Antwort aufgehört.';
@override
String get assistantNoAnswer => 'Keine Antwort.';
@override
String get assistantCopyAnswer => 'Antwort kopieren';
@override
String get assistantCopied => 'Antwort kopiert';
@override
String assistantLookedUpStudysets(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count Lernsets',
one: '1 Lernset',
zero: 'Keine Lernsets',
);
return '$_temp0 aufgelistet';
}
@override
String assistantLookedUpCards(String query, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count gefunden',
one: '1 gefunden',
zero: 'keine gefunden',
);
return 'Karten nach „$query“ durchsucht: $_temp0';
}
@override
String assistantLookedUpNotes(String query, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count gefunden',
one: '1 gefunden',
zero: 'keine gefunden',
);
return 'Notizen nach „$query“ durchsucht: $_temp0';
}
@override
String get assistantLookedUpNote => 'Eine Notiz gelesen';
@override
String get assistantLookedUpNoNote => 'Eine Notiz gesucht, keine gefunden';
@override
String get settingsSearchEngine => 'Suchmaschine';

View file

@ -6652,6 +6652,160 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get assistantSearchFailed => 'The search couldn\'t be opened.';
@override
String get assistantChoose => 'Who answers';
@override
String get assistantWebSearch => 'Web search';
@override
String get assistantChooseAgree => 'Read and agree first';
@override
String get assistantChooseOwn => 'With your key';
@override
String assistantHintAsk(String name) {
return 'Ask $name…';
}
@override
String get assistantSendAsk => 'Ask';
@override
String get assistantScope => 'What it may look up';
@override
String get assistantScopeTicked => 'Only what I choose';
@override
String get assistantScopeStudyset => 'This studyset';
@override
String get assistantScopeTree => 'Its tree';
@override
String get assistantScopeLibrary => 'Library';
@override
String get assistantScopeTickedLong => 'Only what I choose above';
@override
String get assistantScopeStudysetLong => 'This studyset with its topics';
@override
String get assistantScopeTreeLong => 'Its whole tree, from the top';
@override
String get assistantScopeLibraryLong => 'My whole library';
@override
String get assistantScopePrivate => 'Include private studysets';
@override
String assistantScopeWithPrivate(String scope) {
return '$scope, private ones too';
}
@override
String get assistantHistory => 'Conversations';
@override
String get assistantHistoryEmpty => 'No conversations yet';
@override
String get assistantHistoryEmptyHint =>
'Ask an assistant in the field below.';
@override
String get assistantHistoryDelete => 'Delete conversation';
@override
String assistantHistoryDeleteMessage(String title) {
return '“$title” goes from all your devices.';
}
@override
String get assistantHistoryDeleteConfirm => 'Delete';
@override
String get assistantGone => 'An assistant no longer offered';
@override
String get assistantGoneNote =>
'This assistant is no longer offered to you here, so the conversation can\'t go on.';
@override
String get assistantNotKept =>
'Not kept: this device can\'t encrypt conversations, so this one is gone once you close it.';
@override
String get assistantFollowUp => 'Ask more…';
@override
String get assistantStop => 'Stop';
@override
String get assistantAnswering => 'Answering…';
@override
String get assistantStopped => 'Stopped before it answered.';
@override
String get assistantStoppedEarly =>
'It looked things up too often and stopped without an answer.';
@override
String get assistantNoAnswer => 'No answer.';
@override
String get assistantCopyAnswer => 'Copy the answer';
@override
String get assistantCopied => 'Answer copied';
@override
String assistantLookedUpStudysets(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count studysets',
one: '1 studyset',
zero: 'no studysets',
);
return 'Listed $_temp0';
}
@override
String assistantLookedUpCards(String query, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count found',
one: '1 found',
zero: 'none found',
);
return 'Looked for “$query” in cards: $_temp0';
}
@override
String assistantLookedUpNotes(String query, int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count found',
one: '1 found',
zero: 'none found',
);
return 'Looked for “$query” in notes: $_temp0';
}
@override
String get assistantLookedUpNote => 'Read a note';
@override
String get assistantLookedUpNoNote => 'Looked for a note, found none';
@override
String get settingsSearchEngine => 'Search engine';