From 2d70c6b0ae41c27ad8280ad6535f7ca886fdafc9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 21:06:48 -0800 Subject: [PATCH] Learn: the tabs, skill and tutorial rows and Back run commands The For you and Skills tab buttons, a skill's row, a tutorial's row and Back changed the Learn panel's view directly, so the control channel and MCP could not choose a tab, open a skill's tutorials or go back. - learn.tab {tab: forYou|skills} shows the panel on that tab; the Skills tab keeps the skill and tutorial it had open. - learn.skill {skill} shows the panel on the Skills tab with that skill's tutorials and returns the skill as learn.list lists it. - learn.back {} goes up one level, as Back does. - A tutorial's row runs learn.show. - The commands are listed in both UI command catalogs, and their labels have a row in every catalog. - Docs: docs/mcp.md, docs/target-app-parity.md, docs/ui-parity.md. --- crates/mcp/src/ui_catalog.rs | 18 ++++ crates/ui-egui/src/i18n/bg.tsv | 3 + crates/ui-egui/src/i18n/bs.tsv | 3 + crates/ui-egui/src/i18n/ca.tsv | 3 + crates/ui-egui/src/i18n/cs.tsv | 3 + crates/ui-egui/src/i18n/da.tsv | 3 + crates/ui-egui/src/i18n/de.tsv | 3 + crates/ui-egui/src/i18n/el.tsv | 3 + crates/ui-egui/src/i18n/es.tsv | 3 + crates/ui-egui/src/i18n/fi.tsv | 3 + crates/ui-egui/src/i18n/fr.tsv | 3 + crates/ui-egui/src/i18n/hi.tsv | 3 + crates/ui-egui/src/i18n/hu.tsv | 3 + crates/ui-egui/src/i18n/id.tsv | 3 + crates/ui-egui/src/i18n/it.tsv | 3 + crates/ui-egui/src/i18n/ja.tsv | 3 + crates/ui-egui/src/i18n/ka.tsv | 3 + crates/ui-egui/src/i18n/ko.tsv | 3 + crates/ui-egui/src/i18n/nl.tsv | 3 + crates/ui-egui/src/i18n/pl.tsv | 3 + crates/ui-egui/src/i18n/pt-br.tsv | 3 + crates/ui-egui/src/i18n/ru.tsv | 3 + crates/ui-egui/src/i18n/sv.tsv | 3 + crates/ui-egui/src/i18n/tr.tsv | 3 + crates/ui-egui/src/i18n/uk.tsv | 3 + crates/ui-egui/src/i18n/vi.tsv | 3 + crates/ui-egui/src/i18n/zh-hans.tsv | 3 + crates/ui-egui/src/i18n/zh-hant.tsv | 3 + crates/ui-egui/src/menus/ui_commands.rs | 18 ++++ crates/ui-egui/src/panels/learn.rs | 129 ++++++++++++++++++----- crates/ui-egui/src/panels/learn/tests.rs | 71 +++++++++++++ docs/mcp.md | 14 ++- docs/target-app-parity.md | 2 +- docs/ui-parity.md | 2 +- 34 files changed, 306 insertions(+), 29 deletions(-) diff --git a/crates/mcp/src/ui_catalog.rs b/crates/mcp/src/ui_catalog.rs index e3b57166b7..1be67d191a 100644 --- a/crates/mcp/src/ui_catalog.rs +++ b/crates/mcp/src/ui_catalog.rs @@ -668,18 +668,36 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "", "{} the Learn panel's skills in the order its Skills tab lists them: {skills: [{id, title, tutorials: [{id, title, steps: number of steps}]}]}", ), + ( + "learn.tab", + "Show Learn Tab", + "", + "{tab: forYou|skills} show the Learn panel on its For you or Skills tab, as the tab buttons do; the Skills tab keeps the skill and tutorial it had open; a missing, non-string or unknown tab is an error and changes nothing; returns {tab, skill, tutorial}, with null for no skill or no tutorial open", + ), ( "learn.skills", "Back to Skills", "", "{} show the Learn panel on its Skills tab with the list of skills, with no skill and no tutorial open, as Back to skills after a skill's last tutorial and the Quick skill guide card do; returns what learn.list returns", ), + ( + "learn.skill", + "Show Skill", + "", + "{skill: id (see learn.list)} show the Learn panel on its Skills tab with that skill's tutorials, as choosing the skill's row does; a missing, non-string or unknown id is an error and changes nothing; returns the skill as learn.list lists it: {id, title, tutorials: [{id, title, steps}]}", + ), ( "learn.show", "Show Tutorial", "", "{tutorial: id (see learn.list)} show the Learn panel on its Skills tab with that tutorial's numbered steps, as choosing its skill and then the tutorial does; a missing, non-string or unknown id is an error and changes nothing; returns {skill, tutorial, title, steps: [text]}", ), + ( + "learn.back", + "Back One Level", + "", + "{} show the Learn panel and go up one level on its Skills tab, as its Back button does: from a tutorial's steps to its skill's tutorials, and from a skill's tutorials to the list of skills; on the list of skills or the For you tab the view stays as it is; returns {tab, skill, tutorial}, as learn.tab does", + ), ( "learn.locateTool", "Show Tool Location", diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 1f469c69c9..5842cecd62 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -6854,6 +6854,9 @@ breakText Glyphs Глифи Open Tutorial Sample Отваряне на примерния файл на урока Finish Tutorial Завършване на урок Next Tutorial Следващ урок + Show Learn Tab Показване на раздел в Обучение + Show Skill Показване на умение + Back One Level Едно ниво назад Back to Skills Обратно към уменията @msg tutorial `{id}` has no practice sample урокът `{id}` няма примерен файл за упражнение @msg open the practice sample of `{id}` first (see learn.openSample) първо отворете примерния файл за упражнение на `{id}` (вижте learn.openSample) diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index a23133933a..1c92ce132a 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -6907,6 +6907,9 @@ shape Open Otvoren Open Tutorial Sample Otvori uzorak tutorijala Finish Tutorial Završi tutorijal Next Tutorial Sljedeći tutorijal + Show Learn Tab Prikaži karticu u Učenju + Show Skill Prikaži vještinu + Back One Level Jedan nivo nazad Back to Skills Nazad na vještine @msg tutorial `{id}` has no practice sample tutorijal `{id}` nema uzorak za vježbu @msg open the practice sample of `{id}` first (see learn.openSample) prvo otvorite uzorak za vježbu tutorijala `{id}` (vidi learn.openSample) diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 373eb0b79d..ac9c01444c 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -7439,6 +7439,9 @@ breakText Glyphs Glifs Open Tutorial Sample Obre la mostra del tutorial Finish Tutorial Acaba el tutorial Next Tutorial Tutorial següent + Show Learn Tab Mostra una pestanya d'Aprèn + Show Skill Mostra l'habilitat + Back One Level Torna un nivell enrere Back to Skills Torna a les habilitats @msg tutorial `{id}` has no practice sample el tutorial `{id}` no té cap mostra de pràctica @msg open the practice sample of `{id}` first (see learn.openSample) obriu primer la mostra de pràctica de `{id}` (vegeu learn.openSample) diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 7178be3143..e7d8d0dc69 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -7458,6 +7458,9 @@ breakText Glyphs Glyfy Open Tutorial Sample Otevřít ukázku výukového kurzu Finish Tutorial Dokončit výukový kurz Next Tutorial Další výukový kurz + Show Learn Tab Zobrazit kartu Výuky + Show Skill Zobrazit dovednost + Back One Level O úroveň zpět Back to Skills Zpět na dovednosti @msg tutorial `{id}` has no practice sample výukový kurz `{id}` nemá cvičnou ukázku @msg open the practice sample of `{id}` first (see learn.openSample) nejprve otevřete cvičnou ukázku kurzu `{id}` (viz learn.openSample) diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 433c6af3d6..a81f22cf25 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -6869,6 +6869,9 @@ breakText Glyphs Glyffer Open Tutorial Sample Åbn eksempel til vejledning Finish Tutorial Afslut vejledning Next Tutorial Næste vejledning + Show Learn Tab Vis fane i Lær + Show Skill Vis færdighed + Back One Level Ét niveau tilbage Back to Skills Tilbage til færdigheder @msg tutorial `{id}` has no practice sample vejledningen `{id}` har intet øvelseseksempel @msg open the practice sample of `{id}` first (see learn.openSample) åbn først øvelseseksemplet til `{id}` (se learn.openSample) diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 951f6f9c37..9dd9430485 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -7054,6 +7054,9 @@ breakText Glyphs Glyphen Open Tutorial Sample Übungsdatei des Lernprogramms öffnen Finish Tutorial Lernprogramm abschließen Next Tutorial Nächstes Lernprogramm + Show Learn Tab Registerkarte in Lernen anzeigen + Show Skill Fertigkeit anzeigen + Back One Level Eine Ebene zurück Back to Skills Zurück zu den Fertigkeiten @msg tutorial `{id}` has no practice sample das Lernprogramm `{id}` hat keine Übungsdatei @msg open the practice sample of `{id}` first (see learn.openSample) öffnen Sie zuerst die Übungsdatei von `{id}` (siehe learn.openSample) diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 62e3adc491..6fc02b5d88 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -7411,6 +7411,9 @@ breakText Glyphs Γλύφοι Open Tutorial Sample Άνοιγμα δείγματος οδηγού εκμάθησης Finish Tutorial Ολοκλήρωση οδηγού εκμάθησης Next Tutorial Επόμενος οδηγός εκμάθησης + Show Learn Tab Εμφάνιση καρτέλας της Εκμάθησης + Show Skill Εμφάνιση δεξιότητας + Back One Level Ένα επίπεδο πίσω Back to Skills Πίσω στις δεξιότητες @msg tutorial `{id}` has no practice sample ο οδηγός εκμάθησης `{id}` δεν έχει δείγμα εξάσκησης @msg open the practice sample of `{id}` first (see learn.openSample) ανοίξτε πρώτα το δείγμα εξάσκησης του `{id}` (δείτε learn.openSample) diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index d1c6aa53af..7502e7cf29 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -7402,6 +7402,9 @@ breakText Glyphs Glifos Open Tutorial Sample Abrir muestra del tutorial Finish Tutorial Terminar tutorial Next Tutorial Tutorial siguiente + Show Learn Tab Mostrar pestaña de Aprender + Show Skill Mostrar habilidad + Back One Level Volver un nivel Back to Skills Volver a las habilidades @msg tutorial `{id}` has no practice sample el tutorial `{id}` no tiene muestra de práctica @msg open the practice sample of `{id}` first (see learn.openSample) abre primero la muestra de práctica de `{id}` (consulta learn.openSample) diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 45e47fd355..dc01161446 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -7493,6 +7493,9 @@ breakText Glyphs Merkit Open Tutorial Sample Avaa opetusohjelman harjoitustiedosto Finish Tutorial Viimeistele opetusohjelma Next Tutorial Seuraava opetusohjelma + Show Learn Tab Näytä Opi-paneelin välilehti + Show Skill Näytä taito + Back One Level Taso takaisin Back to Skills Takaisin taitoihin @msg tutorial `{id}` has no practice sample opetusohjelmalla `{id}` ei ole harjoitustiedostoa @msg open the practice sample of `{id}` first (see learn.openSample) avaa ensin opetusohjelman `{id}` harjoitustiedosto (ks. learn.openSample) diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 057dce6872..f39838414b 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -7453,6 +7453,9 @@ breakText Glyphs Glyphes Open Tutorial Sample Ouvrir le fichier d’exercice du tutoriel Finish Tutorial Terminer le tutoriel Next Tutorial Tutoriel suivant + Show Learn Tab Afficher un onglet d'Apprendre + Show Skill Afficher la compétence + Back One Level Revenir d'un niveau Back to Skills Retour aux compétences @msg tutorial `{id}` has no practice sample le tutoriel `{id}` n’a pas de fichier d’exercice @msg open the practice sample of `{id}` first (see learn.openSample) ouvrez d’abord le fichier d’exercice de `{id}` (voir learn.openSample) diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 9d056e085a..060007e602 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -8849,6 +8849,9 @@ theme Appearance अपीयरेंस Open Tutorial Sample ट्यूटोरियल का नमूना खोलें Finish Tutorial ट्यूटोरियल पूरा करें Next Tutorial अगला ट्यूटोरियल + Show Learn Tab सीखें का टैब दिखाएँ + Show Skill कौशल दिखाएँ + Back One Level एक स्तर वापस Back to Skills कौशल पर वापस जाएँ @msg tutorial `{id}` has no practice sample ट्यूटोरियल `{id}` का कोई अभ्यास नमूना नहीं है @msg open the practice sample of `{id}` first (see learn.openSample) पहले `{id}` का अभ्यास नमूना खोलें (learn.openSample देखें) diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index c2f1a52cd9..b5dcbc4fa0 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -7442,6 +7442,9 @@ breakText Glyphs Glifák Open Tutorial Sample Oktatóanyag mintafájljának megnyitása Finish Tutorial Oktatóanyag befejezése Next Tutorial Következő oktatóanyag + Show Learn Tab Tanulás lap megjelenítése + Show Skill Készség megjelenítése + Back One Level Egy szinttel vissza Back to Skills Vissza a készségekhez @msg tutorial `{id}` has no practice sample nincs gyakorló mintafájl ehhez az oktatóanyaghoz: `{id}` @msg open the practice sample of `{id}` first (see learn.openSample) előbb nyissa meg a gyakorló mintafájlt ehhez: `{id}` (lásd: learn.openSample) diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index a36c629332..8733e7674b 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -6869,6 +6869,9 @@ breakText Glyphs Glif Open Tutorial Sample Buka contoh tutorial Finish Tutorial Selesaikan tutorial Next Tutorial Tutorial berikutnya + Show Learn Tab Tampilkan tab Belajar + Show Skill Tampilkan keterampilan + Back One Level Kembali satu tingkat Back to Skills Kembali ke keterampilan @msg tutorial `{id}` has no practice sample tutorial `{id}` tidak memiliki contoh latihan @msg open the practice sample of `{id}` first (see learn.openSample) buka contoh latihan `{id}` terlebih dahulu (lihat learn.openSample) diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 0ac2816b07..48eddf66d6 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -7088,6 +7088,9 @@ breakText Glyphs Glifi Open Tutorial Sample Apri l’esempio del tutorial Finish Tutorial Termina il tutorial Next Tutorial Tutorial successivo + Show Learn Tab Mostra una scheda di Impara + Show Skill Mostra competenza + Back One Level Indietro di un livello Back to Skills Torna alle competenze @msg tutorial `{id}` has no practice sample il tutorial `{id}` non ha un esempio di esercitazione @msg open the practice sample of `{id}` first (see learn.openSample) apri prima l’esempio di esercitazione di `{id}` (vedi learn.openSample) diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 71de1ce91c..61e1b4d8d7 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -7154,6 +7154,9 @@ breakText Glyphs 字形 Open Tutorial Sample チュートリアルのサンプルを開く Finish Tutorial チュートリアルを完了 Next Tutorial 次のチュートリアル + Show Learn Tab 学ぶのタブを表示 + Show Skill スキルを表示 + Back One Level 1 つ上の階層に戻る Back to Skills スキルに戻る @msg tutorial `{id}` has no practice sample チュートリアル `{id}` には練習用サンプルがありません @msg open the practice sample of `{id}` first (see learn.openSample) 先に `{id}` の練習用サンプルを開いてください (learn.openSample を参照) diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 2cff51d94b..738dbcb6bf 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -7572,6 +7572,9 @@ font-script Japanese იაპონური Open Tutorial Sample გაკვეთილის ნიმუშის გახსნა Finish Tutorial გაკვეთილის დასრულება Next Tutorial შემდეგი გაკვეთილი + Show Learn Tab სწავლის ჩანართის ჩვენება + Show Skill უნარის ჩვენება + Back One Level ერთი დონით უკან Back to Skills უნარებზე დაბრუნება @msg tutorial `{id}` has no practice sample გაკვეთილს `{id}` არ აქვს სავარჯიშო ნიმუში @msg open the practice sample of `{id}` first (see learn.openSample) ჯერ გახსენით `{id}` გაკვეთილის სავარჯიშო ნიმუში (იხ. learn.openSample) diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 4715a413f4..ec19a6b246 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -7049,6 +7049,9 @@ breakText Glyphs 글리프 Open Tutorial Sample 튜토리얼 샘플 열기 Finish Tutorial 튜토리얼 완료 Next Tutorial 다음 튜토리얼 + Show Learn Tab 학습 탭 표시 + Show Skill 기술 표시 + Back One Level 한 단계 뒤로 Back to Skills 기술로 돌아가기 @msg tutorial `{id}` has no practice sample 튜토리얼 `{id}`에 연습용 샘플이 없습니다 @msg open the practice sample of `{id}` first (see learn.openSample) 먼저 `{id}`의 연습용 샘플을 여세요(learn.openSample 참조) diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 94957fb782..b70ec686a4 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -7491,6 +7491,9 @@ breakText Glyphs Glyphs Open Tutorial Sample Oefenbestand van tutorial openen Finish Tutorial Tutorial voltooien Next Tutorial Volgende tutorial + Show Learn Tab Tabblad van Leren tonen + Show Skill Vaardigheid tonen + Back One Level Eén niveau terug Back to Skills Terug naar vaardigheden @msg tutorial `{id}` has no practice sample tutorial `{id}` heeft geen oefenbestand @msg open the practice sample of `{id}` first (see learn.openSample) open eerst het oefenbestand van `{id}` (zie learn.openSample) diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 48f2671c2d..66a17f4702 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -7315,6 +7315,9 @@ breakText Glyphs Glify Open Tutorial Sample Otwórz przykład samouczka Finish Tutorial Zakończ samouczek Next Tutorial Następny samouczek + Show Learn Tab Pokaż kartę panelu Nauka + Show Skill Pokaż umiejętność + Back One Level Wróć o jeden poziom Back to Skills Powrót do umiejętności @msg tutorial `{id}` has no practice sample samouczek `{id}` nie ma przykładu do ćwiczeń @msg open the practice sample of `{id}` first (see learn.openSample) najpierw otwórz przykład do ćwiczeń samouczka `{id}` (patrz learn.openSample) diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index f6e54552d4..34b8ed6883 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -7531,6 +7531,9 @@ swatch Yellow Green Verde-amarelado Open Tutorial Sample Abrir Amostra do Tutorial Finish Tutorial Concluir Tutorial Next Tutorial Próximo Tutorial + Show Learn Tab Mostrar Guia de Aprender + Show Skill Mostrar Habilidade + Back One Level Voltar um Nível Back to Skills Voltar às Habilidades @msg tutorial `{id}` has no practice sample o tutorial `{id}` não tem amostra de prática @msg open the practice sample of `{id}` first (see learn.openSample) abra primeiro a amostra de prática de `{id}` (veja learn.openSample) diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 8dc8336089..86b324b142 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -7547,6 +7547,9 @@ shape Open Открытый Open Tutorial Sample Открыть пример урока Finish Tutorial Завершить урок Next Tutorial Следующий урок + Show Learn Tab Показать вкладку панели «Обучение» + Show Skill Показать навык + Back One Level На уровень назад Back to Skills Назад к навыкам @msg tutorial `{id}` has no practice sample у урока `{id}` нет учебного примера @msg open the practice sample of `{id}` first (see learn.openSample) сначала откройте учебный пример урока `{id}` (см. learn.openSample) diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index cfb9d9734b..cc7bffb639 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -8896,6 +8896,9 @@ digits Hindi Hindi Open Tutorial Sample Öppna övningsexempel för självstudie Finish Tutorial Slutför självstudie Next Tutorial Nästa självstudie + Show Learn Tab Visa flik i Lär dig + Show Skill Visa färdighet + Back One Level En nivå tillbaka Back to Skills Tillbaka till färdigheter @msg tutorial `{id}` has no practice sample självstudien `{id}` har inget övningsexempel @msg open the practice sample of `{id}` first (see learn.openSample) öppna först övningsexemplet för `{id}` (se learn.openSample) diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 2b3e843ba3..b216c2a995 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -7414,6 +7414,9 @@ breakText Glyphs Glifler Open Tutorial Sample Öğretici Örneğini Aç Finish Tutorial Öğreticiyi Bitir Next Tutorial Sonraki Öğretici + Show Learn Tab Öğren Sekmesini Göster + Show Skill Beceriyi Göster + Back One Level Bir Düzey Geri Back to Skills Becerilere Dön @msg tutorial `{id}` has no practice sample `{id}` öğreticisinin alıştırma örneği yok @msg open the practice sample of `{id}` first (see learn.openSample) önce `{id}` öğreticisinin alıştırma örneğini açın (bkz. learn.openSample) diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 50690fa0c7..cd72e5a83f 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -7068,6 +7068,9 @@ breakText Glyphs Гліфи Open Tutorial Sample Відкрити приклад уроку Finish Tutorial Завершити урок Next Tutorial Наступний урок + Show Learn Tab Показати вкладку панелі «Навчання» + Show Skill Показати навичку + Back One Level На рівень назад Back to Skills Назад до навичок @msg tutorial `{id}` has no practice sample урок `{id}` не має навчального прикладу @msg open the practice sample of `{id}` first (see learn.openSample) спочатку відкрийте навчальний приклад уроку `{id}` (див. learn.openSample) diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index d8469bce53..b7eb074068 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -7069,6 +7069,9 @@ breakText Glyphs Glyph Open Tutorial Sample Mở tệp mẫu của hướng dẫn Finish Tutorial Hoàn tất hướng dẫn Next Tutorial Hướng dẫn tiếp theo + Show Learn Tab Hiện thẻ của Học + Show Skill Hiện kỹ năng + Back One Level Quay lại một cấp Back to Skills Quay lại kỹ năng @msg tutorial `{id}` has no practice sample hướng dẫn `{id}` không có tệp mẫu thực hành @msg open the practice sample of `{id}` first (see learn.openSample) hãy mở tệp mẫu thực hành của `{id}` trước (xem learn.openSample) diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index d9d9110276..1d7f4ead53 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -7297,6 +7297,9 @@ breakText Glyphs 字形 Open Tutorial Sample 打开教程示例 Finish Tutorial 完成教程 Next Tutorial 下一个教程 + Show Learn Tab 显示学习选项卡 + Show Skill 显示技能 + Back One Level 返回上一级 Back to Skills 返回技能 @msg tutorial `{id}` has no practice sample 教程 `{id}` 没有练习示例 @msg open the practice sample of `{id}` first (see learn.openSample) 请先打开 `{id}` 的练习示例 (请参阅 learn.openSample) diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index a0d987fe69..213c80600e 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -7104,6 +7104,9 @@ breakText Glyphs 字符 Open Tutorial Sample 開啟教學課程範例 Finish Tutorial 完成教學課程 Next Tutorial 下一個教學課程 + Show Learn Tab 顯示學習索引標籤 + Show Skill 顯示技能 + Back One Level 返回上一層 Back to Skills 返回技能 @msg tutorial `{id}` has no practice sample 教學課程 `{id}` 沒有練習範例 @msg open the practice sample of `{id}` first (see learn.openSample) 請先開啟 `{id}` 的練習範例 (請參閱 learn.openSample) diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index cd311d45a8..af474fb4f7 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -669,18 +669,36 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "", "{} the Learn panel's skills in the order its Skills tab lists them: {skills: [{id, title, tutorials: [{id, title, steps: number of steps}]}]}", ), + ( + "learn.tab", + "Show Learn Tab", + "", + "{tab: forYou|skills} show the Learn panel on its For you or Skills tab, as the tab buttons do; the Skills tab keeps the skill and tutorial it had open; a missing, non-string or unknown tab is an error and changes nothing; returns {tab, skill, tutorial}, with null for no skill or no tutorial open", + ), ( "learn.skills", "Back to Skills", "", "{} show the Learn panel on its Skills tab with the list of skills, with no skill and no tutorial open, as Back to skills after a skill's last tutorial and the Quick skill guide card do; returns what learn.list returns", ), + ( + "learn.skill", + "Show Skill", + "", + "{skill: id (see learn.list)} show the Learn panel on its Skills tab with that skill's tutorials, as choosing the skill's row does; a missing, non-string or unknown id is an error and changes nothing; returns the skill as learn.list lists it: {id, title, tutorials: [{id, title, steps}]}", + ), ( "learn.show", "Show Tutorial", "", "{tutorial: id (see learn.list)} show the Learn panel on its Skills tab with that tutorial's numbered steps, as choosing its skill and then the tutorial does; a missing, non-string or unknown id is an error and changes nothing; returns {skill, tutorial, title, steps: [text]}", ), + ( + "learn.back", + "Back One Level", + "", + "{} show the Learn panel and go up one level on its Skills tab, as its Back button does: from a tutorial's steps to its skill's tutorials, and from a skill's tutorials to the list of skills; on the list of skills or the For you tab the view stays as it is; returns {tab, skill, tutorial}, as learn.tab does", + ), ( "learn.locateTool", "Show Tool Location", diff --git a/crates/ui-egui/src/panels/learn.rs b/crates/ui-egui/src/panels/learn.rs index ee7967c8a3..ff61011fe4 100644 --- a/crates/ui-egui/src/panels/learn.rs +++ b/crates/ui-egui/src/panels/learn.rs @@ -5,8 +5,9 @@ //! tutorial previews its numbered steps. A tool's name in a step is a link that shows where the tool //! is ([`locate`]). Back goes up one level. Under the steps, View more opens the tutorial's practice //! sample as a new untitled document; Finish tutorial then marks it finished, and Next Up shows the -//! next tutorial in the same skill. `learn.list` lists the tutorials, `learn.skills` shows the list -//! of skills, `learn.show` opens one tutorial, `learn.locateTool` shows where a tool is, +//! next tutorial in the same skill. `learn.list` lists the tutorials, `learn.tab` chooses a tab, +//! `learn.skills` shows the list of skills, `learn.skill` opens one skill, `learn.show` opens one +//! tutorial, `learn.back` goes up one level, `learn.locateTool` shows where a tool is, //! `learn.openSample`, `learn.finish` and `learn.next` run the three practice controls, and //! `window.learn` shows or hides the panel. @@ -48,14 +49,23 @@ pub struct Practice { pub finished: Vec<&'static str>, } +/// Each tab with the name `learn.tab` takes for it. +const TABS: [(Tab, &str); 2] = [(Tab::ForYou, "forYou"), (Tab::Skills, "skills")]; + pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { + let mut chosen = None; ui.horizontal(|ui| { - for (tab, label) in [(Tab::ForYou, tl!("For you")), (Tab::Skills, tl!("Skills"))] { + for ((tab, name), label) in TABS.into_iter().zip([tl!("For you"), tl!("Skills")]) { if ui.selectable_label(app.ui.learn.tab == tab, label).clicked() { - app.ui.learn.tab = tab; + chosen = Some(name); } } }); + if let Some(name) = chosen + && let Err(e) = app.run("learn.tab", json!({ "tab": name })) + { + app.status(e); + } crate::widgets::divider(ui); match app.ui.learn.tab { Tab::ForYou => for_you(app, ui), @@ -97,36 +107,40 @@ fn for_you(app: &mut VectorcraftApp, ui: &mut Ui) { /// The Skills tab: the skills, a skill's tutorials, or a tutorial's steps. fn skills(app: &mut VectorcraftApp, ui: &mut Ui) { + let mut run = None; let open = app.ui.learn.tutorial.and_then(catalog::tutorial); - if let Some((skill, tutorial)) = open { + if let Some((_, tutorial)) = open { if back(ui) { - app.ui.learn.skill = Some(skill.id); - app.ui.learn.tutorial = None; + run = Some(("learn.back", json!({}))); } steps(app, ui, tutorial); practice(app, ui, tutorial); - return; - } - if let Some(skill) = app.ui.learn.skill.and_then(catalog::skill) { + } else if let Some(skill) = app.ui.learn.skill.and_then(catalog::skill) { if back(ui) { - app.ui.learn.skill = None; + run = Some(("learn.back", json!({}))); } crate::widgets::subheader(ui, skill.title); for tutorial in skill.tutorials { if row(ui, tutorial.title, None).clicked() { - app.ui.learn.tutorial = Some(tutorial.id); + run = Some(("learn.show", json!({ "tutorial": tutorial.id }))); } } - return; - } - for skill in catalog::SKILLS { - if row(ui, skill.title, Some(skill.tutorials.len())).clicked() { - app.ui.learn.skill = Some(skill.id); + } else { + for skill in catalog::SKILLS { + if row(ui, skill.title, Some(skill.tutorials.len())).clicked() { + run = Some(("learn.skill", json!({ "skill": skill.id }))); + } } } + if let Some((cmd, params)) = run + && let Err(e) = app.run(cmd, params) + { + app.status(e); + } } -/// The Back button above a skill's tutorials and a tutorial's steps; true when it was clicked. +/// The Back button above a skill's tutorials and a tutorial's steps (`learn.back`); true when it +/// was clicked. fn back(ui: &mut Ui) -> bool { crate::widgets::icon_button(ui, "chevron-left", tl!("Back"), false, 22.0).clicked() } @@ -251,14 +265,18 @@ fn row(ui: &mut Ui, title: &str, tutorials: Option) -> egui::Response { resp.on_hover_cursor(egui::CursorIcon::PointingHand) } -/// The Learn panel's UI commands: `window.learn`, `learn.list`, `learn.skills`, `learn.show`, -/// `learn.locateTool`, `learn.openSample`, `learn.finish` and `learn.next`. +/// The Learn panel's UI commands: `window.learn`, `learn.list`, `learn.tab`, `learn.skills`, +/// `learn.skill`, `learn.show`, `learn.back`, `learn.locateTool`, `learn.openSample`, `learn.finish` +/// and `learn.next`. pub(crate) fn command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option> { Some(match id { "window.learn" => toggle(app, p), "learn.list" => Ok(list()), + "learn.tab" => tab(app, p), "learn.skills" => skills_list(app), + "learn.skill" => open_skill(app, p), "learn.show" => open(app, p), + "learn.back" => back_up(app), "learn.locateTool" => locate::locate_tool(app, p), "learn.openSample" => open_sample(app, p), "learn.finish" => finish(app, p), @@ -289,11 +307,72 @@ fn toggle(app: &mut VectorcraftApp, p: &Value) -> Result { /// `learn.list`: every skill with its tutorials and their step counts. fn list() -> Value { - let skills = catalog::SKILLS.iter().map(|s| { - let tutorials: Vec = s.tutorials.iter().map(|t| json!({"id": t.id, "title": t.title, "steps": t.steps.len()})).collect(); - json!({"id": s.id, "title": s.title, "tutorials": tutorials}) - }); - json!({ "skills": skills.collect::>() }) + json!({ "skills": catalog::SKILLS.iter().map(skill_json).collect::>() }) +} + +/// A skill as `learn.list` lists it: `{id, title, tutorials: [{id, title, steps}]}`. +fn skill_json(s: &catalog::Skill) -> Value { + let tutorials: Vec = s.tutorials.iter().map(|t| json!({"id": t.id, "title": t.title, "steps": t.steps.len()})).collect(); + json!({"id": s.id, "title": s.title, "tutorials": tutorials}) +} + +/// What the panel shows: `{tab: forYou|skills, skill, tutorial}`, with null for no skill or no +/// tutorial open. +fn view_json(view: &View) -> Value { + let tab = TABS.iter().find(|(t, _)| *t == view.tab).map(|(_, name)| *name); + json!({"tab": tab, "skill": view.skill, "tutorial": view.tutorial}) +} + +/// `learn.tab {tab}`: the For you and Skills tabs. Show the panel on that tab (`forYou` or +/// `skills`); the Skills tab keeps the skill and tutorial it had open → the view ([`view_json`]). A +/// missing or unknown tab is an error and changes nothing. +fn tab(app: &mut VectorcraftApp, p: &Value) -> Result { + let key = "tab"; + let name = match p.get(key) { + None | Some(Value::Null) => return Err(format!("missing `{key}`")), + Some(Value::String(name)) => name.as_str(), + Some(_) => return Err(format!("`{key}` must be a string")), + }; + let names = || TABS.map(|(_, name)| name).join(", "); + let tab = TABS.iter().find(|(_, n)| *n == name).map(|(t, _)| *t).ok_or_else(|| format!("`{key}` must be one of {}", names()))?; + crate::panel_docking::reveal_panel(app, ID)?; + app.ui.learn.tab = tab; + Ok(view_json(&app.ui.learn)) +} + +/// `learn.skill {skill}`: a skill's row on the Skills tab. Show the panel on the Skills tab with +/// that skill's tutorials → the skill, as `learn.list` lists it. A missing or unknown id is an error +/// and changes nothing. +fn open_skill(app: &mut VectorcraftApp, p: &Value) -> Result { + let key = "skill"; + let id = match p.get(key) { + None | Some(Value::Null) => return Err(format!("missing `{key}`")), + Some(Value::String(id)) => id.as_str(), + Some(_) => return Err(format!("`{key}` must be a string")), + }; + let ids = || catalog::SKILLS.iter().map(|s| s.id).collect::>().join(", "); + let skill = catalog::skill(id).ok_or_else(|| format!("`{key}` must be one of {}", ids()))?; + crate::panel_docking::reveal_panel(app, ID)?; + app.ui.learn = View { tab: Tab::Skills, skill: Some(skill.id), tutorial: None }; + Ok(skill_json(skill)) +} + +/// `learn.back`: the Back button on the Skills tab. Show the panel and go up one level: from a +/// tutorial's steps to its skill's tutorials, and from a skill's tutorials to the list of skills → +/// the view ([`view_json`]). On the list of skills or the For you tab the view stays as it is. +fn back_up(app: &mut VectorcraftApp) -> Result { + crate::panel_docking::reveal_panel(app, ID)?; + let view = &mut app.ui.learn; + if view.tab == Tab::Skills { + if let Some((skill, _)) = view.tutorial.and_then(catalog::tutorial) { + view.skill = Some(skill.id); + view.tutorial = None; + } else if view.skill.is_some() { + view.skill = None; + view.tutorial = None; + } + } + Ok(view_json(view)) } /// `learn.skills`: Back to skills, and the Quick skill guide card. Show the panel on the Skills tab diff --git a/crates/ui-egui/src/panels/learn/tests.rs b/crates/ui-egui/src/panels/learn/tests.rs index 4bd00648d1..c49305920f 100644 --- a/crates/ui-egui/src/panels/learn/tests.rs +++ b/crates/ui-egui/src/panels/learn/tests.rs @@ -493,6 +493,77 @@ fn learn_show_rejects_a_missing_or_unknown_tutorial() { } } +/// The tab buttons, a skill's row, a tutorial's row and Back run `learn.tab`, `learn.skill`, +/// `learn.show` and `learn.back`: each one also shows a panel that was hidden, as the command does. +#[test] +fn the_tabs_rows_and_back_run_their_commands() { + for cmd in ["learn.tab", "learn.skill", "learn.back"] { + assert!(crate::menus::UI_COMMANDS.iter().any(|c| c.0 == cmd), "{cmd} is in the UI command catalog"); + } + let mut app = app(); + let ctx = context(); + let (skill, tutorial) = catalog::tutorial("gradient").unwrap(); + let press = |app: &mut VectorcraftApp, name: &str, view: View| { + app.run("window.learn", json!({"show": false})).unwrap(); + assert!(!shown(app)); + click(app, &ctx, name, show); + assert_eq!(app.ui.learn, view, "{name}"); + assert!(shown(app), "{name} runs its command: {}", app.ui.status); + }; + press(&mut app, "Skills", View { tab: Tab::Skills, skill: None, tutorial: None }); + press(&mut app, skill.title, View { tab: Tab::Skills, skill: Some(skill.id), tutorial: None }); + press(&mut app, tutorial.title, View { tab: Tab::Skills, skill: Some(skill.id), tutorial: Some(tutorial.id) }); + press(&mut app, "For you", View { tab: Tab::ForYou, skill: Some(skill.id), tutorial: Some(tutorial.id) }); + press(&mut app, "Skills", View { tab: Tab::Skills, skill: Some(skill.id), tutorial: Some(tutorial.id) }); + press(&mut app, "Back", View { tab: Tab::Skills, skill: Some(skill.id), tutorial: None }); + press(&mut app, "Back", View { tab: Tab::Skills, skill: None, tutorial: None }); +} + +/// `learn.tab` switches tabs and keeps what the Skills tab had open; `learn.skill` opens a skill's +/// tutorials and answers the skill as `learn.list` lists it; `learn.back` goes up one level and +/// keeps the view on the list of skills and on the For you tab. +#[test] +fn learn_tab_skill_and_back_move_the_view() { + let mut app = app(); + let list = app.run("learn.list", json!({})).unwrap(); + assert_eq!(app.run("learn.skill", json!({"skill": "color"})).unwrap(), list["skills"][2]); + assert!(shown(&app)); + assert_eq!(app.ui.learn, View { tab: Tab::Skills, skill: Some("color"), tutorial: None }); + app.run("learn.show", json!({"tutorial": "gradient"})).unwrap(); + let view = json!({"tab": "forYou", "skill": "color", "tutorial": "gradient"}); + assert_eq!(app.run("learn.tab", json!({"tab": "forYou"})).unwrap(), view); + assert_eq!(app.run("learn.back", json!({})).unwrap(), view, "Back on the For you tab keeps the view"); + assert_eq!(app.run("learn.tab", json!({"tab": "skills"})).unwrap(), json!({"tab": "skills", "skill": "color", "tutorial": "gradient"})); + assert_eq!(app.run("learn.back", json!({})).unwrap(), json!({"tab": "skills", "skill": "color", "tutorial": null})); + let skills = json!({"tab": "skills", "skill": null, "tutorial": null}); + assert_eq!(app.run("learn.back", json!({})).unwrap(), skills); + assert_eq!(app.run("learn.back", json!({})).unwrap(), skills, "Back on the list of skills keeps the view"); + app.run("window.learn", json!({"show": false})).unwrap(); + assert_eq!(app.run("learn.back", json!({})).unwrap(), skills); + assert!(shown(&app), "learn.back shows the panel"); +} + +/// `learn.tab` and `learn.skill` reject a missing, mistyped or unknown value and change nothing. +#[test] +fn learn_tab_and_learn_skill_reject_a_missing_or_unknown_value() { + let mut app = app(); + let before = app.ui.learn.clone(); + let skills = catalog::SKILLS.iter().map(|s| s.id).collect::>().join(", "); + for (cmd, params, error) in [ + ("learn.tab", json!({}), "missing `tab`".to_string()), + ("learn.tab", json!({"tab": null}), "missing `tab`".to_string()), + ("learn.tab", json!({"tab": 1}), "`tab` must be a string".to_string()), + ("learn.tab", json!({"tab": "Skills"}), "`tab` must be one of forYou, skills".to_string()), + ("learn.skill", json!({}), "missing `skill`".to_string()), + ("learn.skill", json!({"skill": ["draw"]}), "`skill` must be a string".to_string()), + ("learn.skill", json!({"skill": "nope"}), format!("`skill` must be one of {skills}")), + ] { + assert_eq!(app.run(cmd, params.clone()).unwrap_err(), error, "{cmd} {params}"); + assert_eq!(app.ui.learn, before); + assert!(!shown(&app)); + } +} + /// The Pen tutorial's steps, as the panel shows them. fn pen_paths(app: &mut VectorcraftApp, ui: &mut egui::Ui) { app.ui.learn = View { tab: Tab::Skills, skill: Some("draw"), tutorial: Some("pen-paths") }; diff --git a/docs/mcp.md b/docs/mcp.md index f756bc8c97..a9e85a153a 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -2861,8 +2861,15 @@ other value of `show` is an error and changes nothing. `learn.list {}` → `{ski lists them, where `steps` is the number of steps. `learn.skills {}` shows the panel on its Skills tab with the list of skills, with no skill and no tutorial open, as the Quick skill guide card and Back to skills do, and answers what `learn.list` answers. `learn.show {tutorial}` shows the panel on its Skills tab with that -tutorial's steps → `{skill, tutorial, title, steps: [text]}`. A missing, non-string or unknown `tutorial` is an error -and changes nothing. +tutorial's steps → `{skill, tutorial, title, steps: [text]}`; choosing a tutorial's row runs it. A missing, non-string +or unknown `tutorial` is an error and changes nothing. The For you and Skills tab buttons run `learn.tab {tab}`, with +`tab` set to `forYou` or `skills`, which shows the panel on that tab → `{tab, skill, tutorial}`, with null for no +skill or no tutorial open; the Skills tab keeps the skill and tutorial it had open. Choosing a skill's row runs +`learn.skill {skill}`, which shows the panel on the Skills tab with that skill's tutorials → the skill as `learn.list` +lists it. A missing, non-string or unknown `tab` or `skill` is an error and changes nothing. Back runs `learn.back {}`, +which shows the panel and goes up one level on the Skills tab, from a tutorial's steps to its skill's tutorials and +from a skill's tutorials to the list of skills, and answers what `learn.tab` answers; on the list of skills or the For +you tab the view stays as it is. Under a tutorial's steps, View more opens the tutorial's practice sample, a small drawing of VectorCraft's own with the art its steps work on, as a new untitled document that becomes the active one; Save asks where to save it. Once the @@ -2886,6 +2893,9 @@ is an error, and an error changes nothing. Behavior reference: {"name":"invoke_menu","arguments":{"command":"learn.finish","params":{"tutorial":"pen-paths"}}} {"name":"invoke_menu","arguments":{"command":"learn.next","params":{"tutorial":"pen-paths"}}} {"name":"invoke_menu","arguments":{"command":"learn.skills","params":{}}} +{"name":"invoke_menu","arguments":{"command":"learn.skill","params":{"skill":"draw"}}} +{"name":"invoke_menu","arguments":{"command":"learn.back","params":{}}} +{"name":"invoke_menu","arguments":{"command":"learn.tab","params":{"tab":"forYou"}}} ``` A step that names a tool shows the tool's name as a blue link. A click on it, or Enter or Space while it has the diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 296bd84b2b..0f47c6ad5d 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -914,7 +914,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Perspective Distort and Free Distort (`object.distort`) turn each warped object's bounding box as the warp turns the object at its own center, the objects inside groups, compound paths and clip groups and the art of linked opacity masks included, so the next Selection or Free Transform drag uses the tilted box; Object › Transform › Reset Bounding Box squares it again, and corners left where they are add no undo step (VectorCraft's own rule; `bboxrotate::distort_turns_an_upright_box_with_the_warp_at_its_center`, `distort_turns_each_member_and_linked_mask_by_its_own_warp`, `mirrored_and_collapsing_distorts_keep_valid_angles`, `distort_angles_undo_redo_and_reopen_together`, `xform::unchanged_distort_records_no_history`). - The Free Transform widget's Constrain works in Free Distort as well as in Free Transform ([the help page](https://helpx.adobe.com/illustrator/using/tool-techniques/free-transform-tool.html)). With Constrain on, or Shift held, a corner dragged in Free Distort moves only along the line through the opposite corner, in the box's own proportions, and the other three corners stay in place; on a turned box the line runs through the box's own corners. The line is VectorCraft's own rule. Cmd/Ctrl+Alt+Shift on a corner still drags in Perspective Distort, where Constrain stays unavailable (tools `xform::free::tests::free_distort_corner_keeps_to_its_diagonal_with_shift_or_constrain`, `shift_pressed_during_a_free_distort_drag_constrains_it`, `a_turned_box_is_constrained_along_its_own_diagonal`, `along_diagonal_of_a_degenerate_box`; engine `xform::free_transform_constrained_distort_keeps_the_corner_on_its_diagonal`; ui-egui `free_transform::tests::the_widget_sets_the_mode_and_a_corner_drag_then_distorts`). - Native macOS menu bar, vector tool cursors, a ⌘K command palette, middle-button panning with any tool. Native menus explicitly end tracking on command selection, outside window presses and Escape; menu updates wait until tracking ends, and a dismissing press never starts a canvas gesture (M14.8). - - The Learn panel (Window › Learn, or Explore tutorials beside the search icon on the app bar) has a For you tab with a Get started section and a Quick skill guide card, and a Skills tab that lists VectorCraft's own skills with their tutorial counts and empty progress rings; a skill lists its tutorials, a tutorial shows its numbered steps, and Back goes up a level; `learn.list` and `learn.show {tutorial}` script it ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `panels::learn::tests`). + - The Learn panel (Window › Learn, or Explore tutorials beside the search icon on the app bar) has a For you tab with a Get started section and a Quick skill guide card, and a Skills tab that lists VectorCraft's own skills with their tutorial counts and empty progress rings; a skill lists its tutorials, a tutorial shows its numbered steps, and Back goes up a level; `learn.list`, `learn.tab {tab}`, `learn.skill {skill}`, `learn.show {tutorial}` and `learn.back` script it, and the tab buttons, the rows and Back run them ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `panels::learn::tests`). - A tool's name in a Learn tutorial step is a blue link that shows where the tool is without choosing it (`learn.locateTool {tool}`): its toolbar button is marked, a collapsed group's flyout opens with the tool's row marked, a torn-off strip comes to the front, and a tool no shown toolbar holds is marked in the All Tools drawer. The mark, its 3 seconds and the hidden-toolbar cases are VectorCraft's own rules ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `panels::learn::tests`, the `a_learn_link_*` tests in `toolbar::tests`). - Under a tutorial's steps, View more opens the tutorial's own practice sample as a new untitled document, and once the tutorial is finished, Next Up moves to the next tutorial in the same skill, as the help page describes ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html)). The samples are VectorCraft's own drawings. A Finish tutorial button that shows once the sample is open, the untitled copy, Back to skills in place of Next Up after a skill's last tutorial, and Next Up showing the next tutorial without opening its sample are VectorCraft's own rules. `learn.openSample`, `learn.finish`, `learn.next` and `learn.skills` (Back to skills) script them. Not done: the help page's short video, and progress kept between sessions and shown in the skill rings. Regression tests: `view_more_opens_the_tutorial_s_sample_as_a_new_untitled_document`, `finishing_a_tutorial_offers_next_up_in_its_skill`, `the_practice_controls_work_from_the_keyboard`, `the_last_tutorial_of_a_skill_offers_back_to_skills`, `learn_finish_and_learn_next_follow_the_practice`, `learn_skills_shows_the_list_of_skills`, `the_practice_commands_reject_a_missing_or_unknown_tutorial`, `every_tutorial_has_its_own_readable_sample` and `the_next_tutorial_is_the_next_one_in_the_same_skill` in `crates/ui-egui/src/panels/learn/tests.rs`. - The errors of the blend spine commands (Move Spine Point, Reshape Spine Segment, Add Spine Point and Delete Spine Point) show in the status bar in the interface language: each has a row in every catalog that translates all status and error messages and in Bulgarian (`blend_spine_param_errors_are_translated`). diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 62a99726a2..cf470d7a9b 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -383,7 +383,7 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po |---|---|---| | Effect > Warp Options: style previews, orientation choices and percent controls | done | #1760; focused input, mixed-style and history checks passed; original captures reviewed. No geometry or optional Properties changes. | | Command search: Up and Down move through the results, Enter runs the highlighted one | done | #1798 (#1757) | -| Learn panel: Window › Learn and Explore tutorials on the app bar open it; For you holds a Quick skill guide card, Skills lists VectorCraft's own skills with tutorial counts and empty progress rings, a tutorial shows its numbered steps, Back goes up a level; a tool's name in a step is a link that marks the tool where the toolbars show it (its button, its group's flyout, a torn-off strip, a toolbar made with New Toolbar or the All Tools drawer) without choosing it; View more opens the tutorial's practice sample as a new untitled document, Finish tutorial then marks it finished, and Next Up shows the next tutorial in the same skill (no video) | done | [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `learn.list`, `learn.skills`, `learn.show`, `learn.locateTool`, `learn.openSample`, `learn.finish`, `learn.next`; the mark's look and 3-second lifetime are VectorCraft's own rules; `panels::learn::tests`, `toolbar::tests` | +| Learn panel: Window › Learn and Explore tutorials on the app bar open it; For you holds a Quick skill guide card, Skills lists VectorCraft's own skills with tutorial counts and empty progress rings, a tutorial shows its numbered steps, Back goes up a level; a tool's name in a step is a link that marks the tool where the toolbars show it (its button, its group's flyout, a torn-off strip, a toolbar made with New Toolbar or the All Tools drawer) without choosing it; View more opens the tutorial's practice sample as a new untitled document, Finish tutorial then marks it finished, and Next Up shows the next tutorial in the same skill (no video) | done | [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/learn-skills-quick-skill-guide.html); `learn.list`, `learn.tab`, `learn.skills`, `learn.skill`, `learn.show`, `learn.back`, `learn.locateTool`, `learn.openSample`, `learn.finish`, `learn.next`; the mark's look and 3-second lifetime are VectorCraft's own rules; `panels::learn::tests`, `toolbar::tests` | | Pathfinder panel: a group selected alone counts its nested shapes, so an expanded stroked object keeps the Shape Modes enabled | done | #1642 (#1635) | | Selected Grid Repeat shows row, column and corner handles and spacing sliders; bounding-box handles take priority where they meet | done | #2691, `tools/src/repeat_grid.rs`; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html); drags preview and commit `object.repeat.options` as one undo step, Escape cancels; Radial Repeat has no handles yet | | Grid Repeat Options (Object › Repeat › Options…) has a Preview checkbox: the repeat on the canvas follows the fields while the dialog is open, Cancel puts the original back without a history step, OK keeps the previewed values as one undo step | done | #2651; [public behavior reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html) lists the Preview option for the grid options only, so Radial and Mirror Repeat Options have none; `dialogs/command.rs` tests |