From 07d771220aba689f1d423845cfff871a97b83d85 Mon Sep 17 00:00:00 2001 From: Danya Ulianchenko Date: Mon, 12 Oct 2026 02:06:41 +0300 Subject: [PATCH] Clean --- .gitignore | 1 + 25.09.2026.md | 140 ------------------- FEATURE_REQUEST_2026-09.md | 96 ------------- NEXT_AGENT_GUIDE.md | 280 ------------------------------------- README.md | 62 -------- ULTRALIGHT_AGENT_GUIDE.md | 70 ---------- ULTRALIGHT_EVAL_RESULTS.md | 82 ----------- foxbrowser_agent_prompt.md | 96 ------------- peergrob.conf | 11 -- 9 files changed, 1 insertion(+), 837 deletions(-) delete mode 100644 25.09.2026.md delete mode 100644 FEATURE_REQUEST_2026-09.md delete mode 100644 NEXT_AGENT_GUIDE.md delete mode 100644 README.md delete mode 100644 ULTRALIGHT_AGENT_GUIDE.md delete mode 100644 ULTRALIGHT_EVAL_RESULTS.md delete mode 100644 foxbrowser_agent_prompt.md delete mode 100644 peergrob.conf diff --git a/.gitignore b/.gitignore index 1a354a3..27b7865 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin/ obj/ *.user *.suo +AI/ diff --git a/25.09.2026.md b/25.09.2026.md deleted file mode 100644 index c157aa7..0000000 --- a/25.09.2026.md +++ /dev/null @@ -1,140 +0,0 @@ -УВАГА: Термінал агента не працює. Не виконуй жодних команд (dotnet build, -dotnet restore, dotnet run, встановлення NuGet-пакетів тощо) самостійно. -Якщо для задачі щось треба встановити, оновити чи запустити — напиши це -явним окремим пунктом "Потрібно виконати вручну:" в кінці відповіді для -цієї задачі, і не намагайся обійти це через bash/термінал. - -Реалізуй дві задачі в FoxBrowser. Роби послідовно, кожну — окремим описом, -що змінено і як перевірити вручну (агент працює в Linux sandbox без -dotnet/WPF runtime — фінальний тест виконує користувач на Windows). - -## 1. Діагностика й виправлення кнопки "Розширення" - -Кнопка налаштувань розширень (`ExtensionsSettingsWindow`) працює -неправильно. Перевір по черзі всі гіпотези нижче, знайди реальну причину -в коді (не вигадуй, яка з них правильна, — прочитай файли й перевір), -і виправ саме те, що підтвердиться. Якщо підтвердиться кілька — виправ усі. - -### 1a. Recovery-fallback environment без AreBrowserExtensionsEnabled - -- Знайти в `MainWindow`/`TabManager` весь код навколо recovery-гілки з - `NEXT_AGENT_GUIDE`/інструкції ("Startup recovery fix — 0x8007139F") — - місце, де при HRESULT `0x8007139F` створюється запасний - `CoreWebView2Environment` з тимчасовою user-data folder. -- Перевірити: чи цей запасний environment створюється через ту саму - функцію/той самий `CoreWebView2EnvironmentOptions` з - `AreBrowserExtensionsEnabled = true`, що й основний, чи через окремий - шматок коду, де цей прапорець забули виставити. -- Якщо прапорець не потрапляє в recovery-гілку — винести створення - `CoreWebView2EnvironmentOptions` в один спільний метод/константу, яку - використовують і основний, і recovery шлях, щоб розбіжність стала - неможливою в принципі, а не просто додати прапорець ще раз у другому місці. -- Додати явний лог/статус (напр. поле `bool ExtensionsAvailable` на - TabManager чи MainWindow), який ExtensionsSettingsWindow може прочитати - при відкритті — і якщо `false` (бо стартанули в recovery-режимі), - показати користувачу зрозуміле повідомлення в самому вікні - ("Розширення недоступні в цьому сеансі — застосунок запустився в - режимі відновлення профілю, перезапусти FoxBrowser"), а не порожній - список без пояснень. - -### 1b. IsEnabled read-only і биндинг чекбокса - -- Знайти XAML розмітку списку розширень в `ExtensionsSettingsWindow.xaml`. -- Перевірити: чи `IsChecked` чекбокса прив'язаний напряму двостороннім - біндингом до `Extension.IsEnabled` (read-only властивість), чи через - `Checked`/`Unchecked` event-обробник (або Command), що явно викликає - `await extension.EnableAsync(bool)`. -- Якщо це прямий `TwoWay`-біндинг на read-only властивість — замінити на - `OneWay`-біндинг для відображення поточного стану + окремий - `Click`/`Checked`/`Unchecked` обробник, який викликає `EnableAsync()`, - чекає результат, і тільки після успіху оновлює UI-стан (checkbox), - а при помилці — повертає checkbox у попередній стан і показує повідомлення. - -### 1c. FolderBrowserDialog без owner-вікна - -- Знайти виклик `FolderBrowserDialog`/`OpenFileDialog` для "Додати - розширення" в `ExtensionsSettingsWindow.xaml.cs`. -- Перевірити, чи передається owner-handle: - `new System.Windows.Interop.WindowInteropHelper(this).Handle` - через `IWin32Window`-обгортку (WinForms-діалог вимагає `IWin32Window`, - не голий `IntPtr` — якщо ще нема обгортки-хелпера, додати простий - `NativeWindowWrapper : IWin32Window`). -- Якщо owner не передається — додати, щоб діалог відкривався поверх - `ExtensionsSettingsWindow`, а не міг ховатись позаду. - -### 1d. Options-сторінка розширення через спільний WebView2 - -- Знайти обробник кнопки "Налаштування" (per-extension), що відкриває - `chrome-extension:///options.html`. -- Перевірити: чи ця навігація йде через ТОЙ САМИЙ спільний `CoreWebView2`, - яким керує `TabManager` для звичайних вкладок, чи через окремий - `CoreWebView2Controller`, створений безпосередньо в - `ExtensionsSettingsWindow`. -- Якщо використовується спільний контрол — це помилка архітектури: клік - по "Налаштування" в діалозі може непомітно перекидати активну вкладку - користувача в головному вікні. Виправити: створити в - `ExtensionsSettingsWindow` власний ізольований `CoreWebView2Controller` - (з того самого `CoreWebView2Environment`, щоб профіль/розширення - збігались) — тільки для options-сторінок, ніколи не займаючи головний - спільний WebView2. - -Після кожного виправленого пункту (1a-1d) — короткий підсумок і ручний -тест-сценарій для Windows. - -## 2. Звук WebView2 у Volume Mixer як "FoxBrowser" - -Додати `Core/AudioSessionLabeler.cs`, який перейменовує WASAPI -audio-сесії власних дочірніх WebView2-процесів на "FoxBrowser" з іконкою -застосунку, БЕЗ ризику торкнутись чужих WebView2-сесій (Teams, Outlook, -системний Edge). - -- Додати NuGet-пакет `NAudio` (зазнач це явно в "Потрібно виконати - вручну" — сам пакет не встановлюй, тільки допиши `` - в `.csproj` вручну і попроси користувача зробити `dotnet restore`). -- Клас має: - 1. Відстежувати список PID усіх дочірніх `msedgewebview2.exe`-процесів, - які належать саме FoxBrowser (ті самі, що вже прив'язані через - Job Object з попередньої задачі про дерево процесів — - переюзати ту логіку, не дублювати). - 2. Через `NAudio.CoreAudioApi.MMDeviceEnumerator` отримати default - render endpoint (`eRender`, `eMultimedia`), і його `AudioSessionManager`. - 3. Підписатись на подію нової сесії (`AudioSessionManager.SessionCreated` - в NAudio, або еквівалент `IAudioSessionNotification.OnSessionCreated` - якщо в NAudio такого немає в потрібній версії — перевір API - конкретної версії пакету, яку додаси). - 4. У обробнику нової сесії: якщо `session.GetProcessID` входить у - список PID FoxBrowser — викликати - `session.DisplayName = "FoxBrowser"` і - `session.IconPath = Path.Combine(AppContext.BaseDirectory, "foxbrowser.ico")` - (або еквівалентні NAudio-методи `SetDisplayNameAsync`/властивості — - перевір актуальний API пакету). - 5. **Критично:** ці властивості НЕ переживають життя об'єкта сесії - (задокументована поведінка WASAPI) — тому одноразового виклику при - старті недостатньо. Логіка з пункту 3-4 має спрацьовувати на КОЖНУ - нову сесію протягом усього часу роботи FoxBrowser, не тільки на старті. - 6. НІКОЛИ не застосовувати `DisplayName`/`IconPath` до сесії, чий PID - не входить у явно відстежуваний список власних дочірніх процесів — - це і є захист від зачіпання чужих `msedgewebview2.exe` на машині - користувача. -- Покласти файл `foxbrowser.ico` в корінь проєкту поруч з іншими іконками - (якщо такого файлу ще нема в репозиторії — створи заглушку-примітивну - іконку і явно напиши користувачу в "Потрібно виконати вручну", що варто - замінити на нормальну). -- Викликати ініціалізацію `AudioSessionLabeler` один раз при старті - застосунку (напр. в `MainWindow` після успішної ініціалізації - `TabManager`, коли вже відомий перший дочірній PID). - -Ручний тест Windows: - -1. Запустити FoxBrowser, відкрити вкладку з YouTube, почати відтворення. -2. Відкрити Volume Mixer (правий клік на іконку гучності в треї Windows → - Open Volume mixer, або через Settings → System → Sound → Volume mixer). -3. Перевірити, що застосунок у списку показаний як "FoxBrowser" з іконкою - застосунку, а не як "Microsoft Edge WebView2"/msedgewebview2.exe. -4. Переключитись на іншу вкладку, знову увімкнути звук на новій — - перевірити, що лейбл лишається "FoxBrowser" і для нової audio-сесії - (не тільки для першої). -5. Паралельно запустити щось інше з WebView2 на цій же машині (напр. - Microsoft Teams, якщо є), перевірити, що ЙОГО лейбл у Volume Mixer - НЕ змінився і лишився своїм — це підтверджує, що фільтр по PID працює - правильно й не займає чужі процеси. \ No newline at end of file diff --git a/FEATURE_REQUEST_2026-09.md b/FEATURE_REQUEST_2026-09.md deleted file mode 100644 index 86d9c27..0000000 --- a/FEATURE_REQUEST_2026-09.md +++ /dev/null @@ -1,96 +0,0 @@ -Реалізуй чотири незалежні покращення в FoxBrowser. Роби їх послідовно, -кожне — окремим комітом, з коротким тестовим сценарієм на кінці. -Не чіпай TabManager/watchdog-логіку, якщо це не потрібно напряму для задачі. - -## 1. Підтримка розширень (Chrome-сумісні extensions) - -WebView2 має нативну підтримку unpacked-розширень через CoreWebView2Profile: - -- При створенні CoreWebView2Environment виставити - `CoreWebView2EnvironmentOptions.AreBrowserExtensionsEnabled = true` - (без цього прапорця AddBrowserExtensionAsync повертає ERROR_NOT_SUPPORTED). -- Додати `Core/ExtensionManager.cs`: - - `AddExtensionAsync(string folderPath)` → `profile.AddBrowserExtensionAsync(folderPath)`. - Важливо: приймається тільки шлях до **розпакованої** папки з manifest.json - (той самий формат, що й Chrome "Load unpacked" у режимі розробника) — - прямого встановлення з Chrome Web Store за URL немає в API, це нормально, - не намагайся емулювати завантаження з магазину. - - `GetInstalledExtensionsAsync()` → `profile.GetBrowserExtensionsAsync()`. - - Enable/disable через `CoreWebView2BrowserExtension.IsEnabled`, видалення — `.Remove()`. -- UI: нове вікно/секція в налаштуваннях `ExtensionsSettingsWindow.xaml` — список - встановлених розширень (назва, іконка якщо є, перемикач увімк/вимк, кнопка - видалити) + кнопка "Додати" з `System.Windows.Forms.FolderBrowserDialog` для - вибору папки розширення. -- Розширення персистяться в профілі (user data folder) автоматично — - не треба вручну зберігати список між запусками, просто перечитувати - GetBrowserExtensionsAsync() при старті. -- ⚠️ Це API позначене в документації Microsoft як таке, що ще може змінюватись - ("prerelease"/experimental) — перевір актуальну версію WebView2 SDK - в csproj, і якщо AddBrowserExtensionAsync недоступний у поточній версії, - онови NuGet-пакет Microsoft.Web.WebView2 до версії, де метод є. - -## 2. Темна смужка заголовка вікна (title bar) - -Стандартний WPF Window малює системну (світлу) title bar незалежно від теми -застосунку. Потрібен P/Invoke до DWM: - -- У `Native/Win32Interop.cs` додати сигнатуру: - `DwmSetWindowAttribute(IntPtr hwnd, int attribute, ref int value, int size)`. -- Атрибут `DWMWA_USE_IMMERSIVE_DARK_MODE` = 20 (Windows 10 build 19041+ і всі - Windows 11); для старіших білдів Windows 10 (до 19041) значення атрибута — 19. - Визнач білд через `Environment.OSVersion` і підстав правильне число, або - просто пробуй 20 і фолбек на 19, якщо виклик повернув помилку. -- Викликати з value=1 (true) у `MainWindow.xaml.cs` в обробнику - `SourceInitialized` (handle вікна вже створений на цьому етапі, - раніше — hwnd ще нульовий і виклик не подіє). -- Якщо застосунок надалі планує світлу/темну тему як перемикач — винести - виклик у окремий метод `ApplyDarkTitleBar(bool enabled)` і викликати його - і при старті, і при зміні теми в налаштуваннях, а не хардкодити один раз. - -## 3. Прибрати кнопку "у трей" з головного вікна - -- У `MainWindow.xaml` знайти і видалити XAML-кнопку, що ховає вікно в трей - (і відповідний Click-обробник у code-behind, якщо він не використовується - більше ніде). -- Функціональність згортання в трей НЕ прибирати — вона має лишитись - доступною через стандартну поведінку: клік на "згорнути" (мінімізувати) - вікна або на "закрити" (якщо зараз close перехоплюється і ховає замість - завершення застосунку — залиш як є). Перевір `TrayIconManager.cs` — - контекстне меню трею (Show/Hide, Exit) лишається без змін. -- Ціль — прибрати саме зайвий UI-елемент з панелі інструментів вікна, - не ламаючи існуючий цикл згортання/розгортання. - -## 4. Виправити подвійний клік по іконці трею - -Зараз: перший клік на іконку в треї лише "проявляє" застосунок на панелі -задач, і тільки другий клік по цій новій іконці на панелі задач реально -відкриває вікно. Це означає, що десь у `TrayIconManager.cs` логіка розбита -на два окремі кроки замість одного atomic-виклику, або що обробник висить -на `MouseDoubleClick` замість одинарного кліку лівою кнопкою, а проміжний -стан (ShowInTaskbar=true) відбувається окремо від Show()/Activate(). - -Виправлення: -- Знайти обробник кліку по NotifyIcon (ймовірно `MouseClick`/`MouseDoubleClick` - на `System.Windows.Forms.NotifyIcon` або аналог з бібліотеки трею, якщо - використовується інша, напр. Hardcodet.NotifyIcon.Wpf). -- Прибрати залежність на подвійний клік — підписатись на одинарний клік - лівою кнопкою (`MouseClick` з перевіркою `e.Button == MouseButtons.Left`, - або відповідний однократний eventArgs у використовуваній бібліотеці). -- В одному обробнику, синхронно й без проміжних станів, виконати весь ланцюжок: -```csharp - mainWindow.ShowInTaskbar = true; - mainWindow.Show(); - mainWindow.WindowState = WindowState.Normal; - mainWindow.Activate(); -``` - Порядок важливий: ShowInTaskbar виставляється ДО Show(), інакше вікно - може з'явитись без іконки на панелі задач до наступного перемальовування. -- Протестувати сценарій: застосунок згорнутий у трей (ShowInTaskbar=false, - Hide()) → один клік лівою кнопкою по іконці трею → вікно одразу видиме, - активне, і з'являється на панелі задач за один крок, без другого кліка. -- Right-click (контекстне меню Show/Hide/Exit) не займати — воно вже працює - окремо і його логіку не чіпати. - -Після кожного пункту напиши короткий підсумок, що саме змінено і як це -перевірити вручну на Windows-машині (агент у Linux sandbox без dotnet/WPF -runtime, тому фінальну компіляцію й ручний тест виконує користувач). \ No newline at end of file diff --git a/NEXT_AGENT_GUIDE.md b/NEXT_AGENT_GUIDE.md deleted file mode 100644 index a236c68..0000000 --- a/NEXT_AGENT_GUIDE.md +++ /dev/null @@ -1,280 +0,0 @@ -# FoxBrowser — інструкція для наступного агента - -## Мета проєкту - -FoxBrowser — Windows-first легкий фоновий браузер для музики/відео. Мета — не зробити ще один Chromium-клон, а зменшити кількість одночасно живого web-коду, renderer-ів, мережевих ресурсів і візуального навантаження. - -Ключовий сценарій: користувач відкриває багато логічних вкладок, але у фоні реально працює одна або кілька медіа-вкладок. Важливо не ламати звук і повертати стан вкладки після перемикання. - -## Стек і запуск - -- C# / .NET 8; -- WPF; -- Microsoft.Web.WebView2 `1.0.2903.40`; -- Windows 10 1809+ і Microsoft Edge WebView2 Runtime. - -Поточний агент працює в Linux sandbox без `dotnet`, Windows SDK та WebView2 Runtime, тому перед релізом потрібно запускати: - -```powershell -dotnet restore -dotnet build -c Debug -dotnet run --project .\FoxBrowser.csproj -``` - -## Що вже зроблено - -### Shell і UI - -- WPF shell, адресний рядок, назад/вперед/оновити; -- логічні вкладки; -- системний трей; -- кнопка `♫ Audio`; -- явна кнопка `Перейти`; -- address bar синхронізується з активною вкладкою і оновлюється після навігації по посиланнях; -- закриття вкладок виправлено на WPF `Button`. - -### Один WebView2 - -`Core/TabManager.cs` використовує **один спільний WebView2**. `BrowserTab` у `Tab.cs` містить тільки metadata: URL, title, scroll position, media position, прапори Audio/playing/loading. - -При перемиканні: - -1. поточний URL/scroll/media state зберігається через JS; -2. активною стає інша логічна вкладка; -3. єдиний WebView2 переходить на її URL; -4. після `NavigationCompleted` відновлюються scroll position, currentTime і playback, якщо попереднє медіа грало. - -### Resource firewall - -Через `WebResourceRequested` блокуються відомі рекламні й аналітичні host-и. У lightweight/minimized режимі блокуються images і fonts. Окремого `CoreWebView2WebResourceContext.Iframe` **не існує**, тому iframe/embed фільтруються тільки консервативно за URL (`/embed/`, `livechat`). Не повертати enum `Iframe` у код. - -### Audio / minimal YouTube - -Кнопка Audio: - -- приховує картинки, SVG, canvas і відеовізуалізацію; -- прибирає частину YouTube UI, рекомендації, comments, chat, Shorts/shelves; -- прибирає preload/prefetch/next; -- залишає media element для звуку. - -CSS/JS не може звільнити базову пам’ять JavaScript runtime YouTube. Це оптимізація трафіку/DOM/GPU, а не повна заміна renderer. - -### Minimized і watchdog - -- при згортанні у трей встановлюється minimized mode; -- нові навігації блокуються, візуальний шар стискається, звук не зупиняється; -- watchdog кожні 30 секунд перевіряє Working Set; -- поріг soft-reload — 850 MB; -- перед reload зберігаються URL, scrollY, currentTime, playing; -- після reload стан відновлюється з повторними спробами через 300 і 1000 мс. - -### Native audio boundary - -`Core/NativeAudioSession.cs` містить `INativeAudioSession` і WPF `MediaPlayer` для **direct audio URL**. Автоматичного YouTube extractor немає. Це навмисно: extractor нестабільний через зміни YouTube, авторизацію, DRM та правила сервісу. - -## Що ще не зроблено - -1. Фінальна компіляція на Windows і тест WebView2 Runtime. -2. Реальний вимір RAM/CPU/GPU у старій і новій архітектурі. -3. Надійне збереження cookies/session state між переходами — профіль WebView2 існує, але стан вкладки поки metadata-only. -4. Повне відновлення плейлистів, form state, history та відкритих popup/window. -5. Відокремлений native YouTube audio backend із optional extractor. -6. Win32 always-on-bottom (`SetWinEventHook`) ще не реалізований. -7. Не можна блокувати всі iframe, scripts або media: YouTube зламається. Firewall має бути allow-by-default, deny тільки для перевірених ресурсів. - -## Відомі ризики - -- `ExecuteScriptAsync` повертає JSON-encoded string; для state parsing використовувати `JsonSerializer.Deserialize(result)`, а не простий `Trim('"')`. -- Перемикання вкладки не повинно повторно запускати navigation через `SelectionChanged`; у `TabManager` є guard для вже активної вкладки. -- Після переходу посиланням address bar оновлюється через `ActiveTabStateChanged`. -- `WasPlaying` не гарантує autoplay: сайт може блокувати `play()` після navigation. -- Сховати `