feat(app): editor blocks: callout, spoiler, formulas, files, video, study button
Blocks beyond the editor's own, each built once for editor and view: - callout: eight kinds in theme colours, kind, title and "folded at first" from its header; written in place, left with Enter on its empty last line; never inside another - spoiler: written in plain sight, hidden behind Reveal read-only - formula on its own line, written on its own screen with a preview and snippet buttons, and formulas within a line (Σ in the format bar); set by flutter_math_fork behind MathView, LaTeX's numbered environments read as their aligned forms - image and sound picked from the device, kept as synced files - YouTube video from its link, with its start and a caption: a card until played, nothing fetched before; played in place where a web view exists (youtube_player_iframe, privacy-enhanced), else opened in the browser, behind YoutubeVideos in Dependencies - study button: a studyset, a tag or all cards, with or without its topics, back first; its StudyFocus rides on the study route and narrows that session only - tables, already in the editor, now insertable everywhere An Insert button opens on the format bar, so blocks can be inserted on phones too; the desktop's / menu lists the same blocks, translated. Card faces now dock the format bar while written, so cards get blocks and formatting on phones as well. The blocks are listed in EditorBlocks: the core's, plus the features' through EditorBlocksScope. Blocks of a type the app doesn't know say they need a newer version and stay as they are, so formatVersion stays 1. Their types and keys are in content_blocks.dart, which RichContent reads: a block's text counts as text, and a video or study button isn't blank. The path import now makes formulas real ones. integration_test/youtube_player_test.dart plays a real video through the app's player on a device, which no widget test can: YouTube refuses embeds that don't say where they are embedded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
9324432f61
commit
ec34bb2334
1 changed files with 5602 additions and 284 deletions
|
|
@ -2842,6 +2842,12 @@ abstract class AppLocalizations {
|
|||
/// **'Cancel'**
|
||||
String get commonCancel;
|
||||
|
||||
/// No description provided for @commonSave.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Save'**
|
||||
String get commonSave;
|
||||
|
||||
/// No description provided for @commonClose.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
@ -2926,6 +2932,522 @@ abstract class AppLocalizations {
|
|||
/// **'This sound can\'t be played. Its file may be damaged, or in a format this device can\'t play.'**
|
||||
String get fileSoundUnplayable;
|
||||
|
||||
/// No description provided for @fileImages.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Images'**
|
||||
String get fileImages;
|
||||
|
||||
/// No description provided for @fileSounds.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Sounds'**
|
||||
String get fileSounds;
|
||||
|
||||
/// No description provided for @fileNotAnImage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This file isn\'t an image the app can show.'**
|
||||
String get fileNotAnImage;
|
||||
|
||||
/// No description provided for @fileNotASound.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This file isn\'t a sound the app can play.'**
|
||||
String get fileNotASound;
|
||||
|
||||
/// No description provided for @fileTooLarge.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This file is larger than {megabytes} MB.'**
|
||||
String fileTooLarge(int megabytes);
|
||||
|
||||
/// No description provided for @fileNotKept.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The file couldn\'t be kept on this device.'**
|
||||
String get fileNotKept;
|
||||
|
||||
/// No description provided for @fileRemoveImage.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove image'**
|
||||
String get fileRemoveImage;
|
||||
|
||||
/// No description provided for @fileRemoveSound.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove sound'**
|
||||
String get fileRemoveSound;
|
||||
|
||||
/// No description provided for @editorInsert.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Insert'**
|
||||
String get editorInsert;
|
||||
|
||||
/// No description provided for @blockGroupText.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Text'**
|
||||
String get blockGroupText;
|
||||
|
||||
/// No description provided for @blockGroupBlocks.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Blocks'**
|
||||
String get blockGroupBlocks;
|
||||
|
||||
/// No description provided for @blockGroupMedia.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Media'**
|
||||
String get blockGroupMedia;
|
||||
|
||||
/// No description provided for @blockHeading.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Heading {level}'**
|
||||
String blockHeading(int level);
|
||||
|
||||
/// No description provided for @blockBulletedList.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Bulleted list'**
|
||||
String get blockBulletedList;
|
||||
|
||||
/// No description provided for @blockNumberedList.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Numbered list'**
|
||||
String get blockNumberedList;
|
||||
|
||||
/// No description provided for @blockChecklist.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Checklist'**
|
||||
String get blockChecklist;
|
||||
|
||||
/// No description provided for @blockQuote.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Quote'**
|
||||
String get blockQuote;
|
||||
|
||||
/// No description provided for @blockDivider.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Divider'**
|
||||
String get blockDivider;
|
||||
|
||||
/// No description provided for @blockTable.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Table'**
|
||||
String get blockTable;
|
||||
|
||||
/// No description provided for @blockRemove.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove'**
|
||||
String get blockRemove;
|
||||
|
||||
/// No description provided for @blockUnknown.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This part needs a newer version of the app.'**
|
||||
String get blockUnknown;
|
||||
|
||||
/// No description provided for @blockCallout.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Callout'**
|
||||
String get blockCallout;
|
||||
|
||||
/// No description provided for @blockSpoiler.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Spoiler'**
|
||||
String get blockSpoiler;
|
||||
|
||||
/// No description provided for @calloutNote.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Note'**
|
||||
String get calloutNote;
|
||||
|
||||
/// No description provided for @calloutInfo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Info'**
|
||||
String get calloutInfo;
|
||||
|
||||
/// No description provided for @calloutTip.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tip'**
|
||||
String get calloutTip;
|
||||
|
||||
/// No description provided for @calloutSuccess.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Success'**
|
||||
String get calloutSuccess;
|
||||
|
||||
/// No description provided for @calloutQuestion.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Question'**
|
||||
String get calloutQuestion;
|
||||
|
||||
/// No description provided for @calloutWarning.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Warning'**
|
||||
String get calloutWarning;
|
||||
|
||||
/// No description provided for @calloutDanger.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Danger'**
|
||||
String get calloutDanger;
|
||||
|
||||
/// No description provided for @calloutQuote.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Quote'**
|
||||
String get calloutQuote;
|
||||
|
||||
/// No description provided for @calloutOptions.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Callout options'**
|
||||
String get calloutOptions;
|
||||
|
||||
/// No description provided for @calloutEditTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit title…'**
|
||||
String get calloutEditTitle;
|
||||
|
||||
/// No description provided for @calloutFoldedAtFirst.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Folded at first'**
|
||||
String get calloutFoldedAtFirst;
|
||||
|
||||
/// No description provided for @calloutRemove.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove callout'**
|
||||
String get calloutRemove;
|
||||
|
||||
/// No description provided for @calloutShow.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Show'**
|
||||
String get calloutShow;
|
||||
|
||||
/// No description provided for @calloutHide.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hide'**
|
||||
String get calloutHide;
|
||||
|
||||
/// No description provided for @calloutWriteInside.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Write inside'**
|
||||
String get calloutWriteInside;
|
||||
|
||||
/// No description provided for @calloutTitleDialog.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Callout title'**
|
||||
String get calloutTitleDialog;
|
||||
|
||||
/// No description provided for @calloutTitleLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Title'**
|
||||
String get calloutTitleLabel;
|
||||
|
||||
/// No description provided for @calloutTitleHelper.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Leave it empty to show the kind\'s name.'**
|
||||
String get calloutTitleHelper;
|
||||
|
||||
/// No description provided for @spoilerReveal.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Reveal'**
|
||||
String get spoilerReveal;
|
||||
|
||||
/// No description provided for @spoilerPlaceholder.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Hidden text, revealed on a tap'**
|
||||
String get spoilerPlaceholder;
|
||||
|
||||
/// No description provided for @blockFormula.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Formula'**
|
||||
String get blockFormula;
|
||||
|
||||
/// No description provided for @formatInlineMath.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Formula in the line'**
|
||||
String get formatInlineMath;
|
||||
|
||||
/// No description provided for @formulaTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Formula'**
|
||||
String get formulaTitle;
|
||||
|
||||
/// No description provided for @formulaInLineTitle.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Formula in the line'**
|
||||
String get formulaInLineTitle;
|
||||
|
||||
/// No description provided for @formulaPreview.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Preview'**
|
||||
String get formulaPreview;
|
||||
|
||||
/// No description provided for @formulaPreviewEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'The formula shows here'**
|
||||
String get formulaPreviewEmpty;
|
||||
|
||||
/// No description provided for @formulaLatex.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'LaTeX'**
|
||||
String get formulaLatex;
|
||||
|
||||
/// No description provided for @formulaLatexHelper.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'\\\\ starts a new line in aligned, cases or a matrix.'**
|
||||
String get formulaLatexHelper;
|
||||
|
||||
/// No description provided for @formulaEmpty.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'An empty formula. Tap to write it.'**
|
||||
String get formulaEmpty;
|
||||
|
||||
/// No description provided for @formulaEdit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit formula'**
|
||||
String get formulaEdit;
|
||||
|
||||
/// No description provided for @formulaRemove.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove formula'**
|
||||
String get formulaRemove;
|
||||
|
||||
/// No description provided for @blockYoutube.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'YouTube video'**
|
||||
String get blockYoutube;
|
||||
|
||||
/// No description provided for @youtubeLink.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'YouTube link'**
|
||||
String get youtubeLink;
|
||||
|
||||
/// No description provided for @youtubeLinkHelper.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Paste a link from YouTube.'**
|
||||
String get youtubeLinkHelper;
|
||||
|
||||
/// No description provided for @youtubeNotFound.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'No YouTube video in this link.'**
|
||||
String get youtubeNotFound;
|
||||
|
||||
/// No description provided for @youtubeFound.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Found the video.'**
|
||||
String get youtubeFound;
|
||||
|
||||
/// No description provided for @youtubeFoundFrom.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Found the video, starting at {time}.'**
|
||||
String youtubeFoundFrom(String time);
|
||||
|
||||
/// No description provided for @youtubeCaption.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Caption'**
|
||||
String get youtubeCaption;
|
||||
|
||||
/// No description provided for @youtubeCaptionHelper.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Shown in place of the video until it plays.'**
|
||||
String get youtubeCaptionHelper;
|
||||
|
||||
/// No description provided for @youtubeVideo.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'YouTube video'**
|
||||
String get youtubeVideo;
|
||||
|
||||
/// No description provided for @youtubeSource.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'YouTube'**
|
||||
String get youtubeSource;
|
||||
|
||||
/// No description provided for @youtubeFrom.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'YouTube · from {time}'**
|
||||
String youtubeFrom(String time);
|
||||
|
||||
/// No description provided for @youtubeEdit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit video'**
|
||||
String get youtubeEdit;
|
||||
|
||||
/// No description provided for @youtubePlay.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Play video'**
|
||||
String get youtubePlay;
|
||||
|
||||
/// No description provided for @youtubeOpen.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Open on YouTube'**
|
||||
String get youtubeOpen;
|
||||
|
||||
/// No description provided for @youtubeRemove.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove video'**
|
||||
String get youtubeRemove;
|
||||
|
||||
/// No description provided for @youtubeBroken.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This video\'s link can\'t be read.'**
|
||||
String get youtubeBroken;
|
||||
|
||||
/// No description provided for @youtubeNotOpened.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Couldn\'t open YouTube.'**
|
||||
String get youtubeNotOpened;
|
||||
|
||||
/// No description provided for @blockStudyButton.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Study button'**
|
||||
String get blockStudyButton;
|
||||
|
||||
/// No description provided for @studyButtonDefaultLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Study'**
|
||||
String get studyButtonDefaultLabel;
|
||||
|
||||
/// No description provided for @studyButtonStudyset.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Studyset to study'**
|
||||
String get studyButtonStudyset;
|
||||
|
||||
/// No description provided for @studyButtonTag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Cards'**
|
||||
String get studyButtonTag;
|
||||
|
||||
/// No description provided for @studyButtonAllCards.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'All cards'**
|
||||
String get studyButtonAllCards;
|
||||
|
||||
/// No description provided for @studyButtonLabel.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Label'**
|
||||
String get studyButtonLabel;
|
||||
|
||||
/// No description provided for @studyButtonTopics.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'With its topics'**
|
||||
String get studyButtonTopics;
|
||||
|
||||
/// No description provided for @studyButtonBackFirst.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Back first'**
|
||||
String get studyButtonBackFirst;
|
||||
|
||||
/// No description provided for @studyButtonBackFirstHelper.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Front/back and vocabulary cards ask their back.'**
|
||||
String get studyButtonBackFirstHelper;
|
||||
|
||||
/// No description provided for @studyButtonWithTag.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Tagged {tag}'**
|
||||
String studyButtonWithTag(String tag);
|
||||
|
||||
/// No description provided for @studyButtonAlone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Without its topics'**
|
||||
String get studyButtonAlone;
|
||||
|
||||
/// No description provided for @studyButtonEdit.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Edit study button'**
|
||||
String get studyButtonEdit;
|
||||
|
||||
/// No description provided for @studyButtonRemove.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Remove study button'**
|
||||
String get studyButtonRemove;
|
||||
|
||||
/// No description provided for @studyButtonBroken.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'This study button doesn\'t say what to study.'**
|
||||
String get studyButtonBroken;
|
||||
|
||||
/// No description provided for @studyButtonGone.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'That studyset no longer exists.'**
|
||||
String get studyButtonGone;
|
||||
|
||||
/// No description provided for @ankiImportMenu.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
|
|
|||
Loading…
Reference in a new issue