Skip to content

feat(shell): ярлык в меню «Пуск» и пробуждение работающей копии (XIC-106) - #104

Merged
Oksion merged 2 commits into
mainfrom
feat/xic-106-start-menu-shortcut
Oct 24, 2026
Merged

Oksion merged 2 commits into
mainfrom
feat/xic-106-start-menu-shortcut

Conversation

@Oksion

@Oksion Oksion commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Закрывает XIC-106: https://linear.app/oksion/issue/XIC-106

Проблема

Пользователи (Redmi Book Pro 2025, TM2409) просят ярлык в меню «Пуск». Пакет winget ставит XiControl как portable, а такой пакет ярлыков не создаёт, инсталлятора у нас нет — найти приложение можно только по exe. Вторая копия при этом молча выходила (Global\XiControlMutex → return), так что повторный запуск не давал ничего, кроме запроса UAC.

Что сделано

  • StartMenuShortcut.cs (новый) — свой .lnk в Start Menu\Programs профиля через IShellLinkW, без зависимостей. Только по явному действию: тумблер или клик по приветствию первого запуска. Состояние — сам файл, поля в конфиге нет. Аргумент --show сразу открывает настройки. Самопочинка на старте по правилам AutoStart: пропавший или более старый exe — переписать, другую папку той же версии не перехватывать (две portable-копии не дерутся).
  • InstanceWake.cs (новый) — будильник второй копии: auto-reset событие Local\XiControlShow создаётся сразу за мьютексом, поэтому ранний сигнал не теряется. Вторая копия делает AllowSetForegroundWindow и сигналит, первая открывает настройки или выводит вперёд уже открытое окно без пересборки. Событие сеансовое при глобальном мьютексе: копия другого пользователя или старая версия будильника не найдёт — тогда сообщение «уже запущен».
  • Program.cs — Main(string[] args); запуск с --autostart по-прежнему молча выходит: задача срабатывает и на возврат в сеанс (ConsoleConnect), иначе окно выскакивало бы на каждом переключении пользователей.
  • TrayApp.cs — AttachWake, предложение ярлыка в приветствии первого запуска, тосты через общий Balloon(...): действие по клику ставится на каждом показе. Раньше _pendingUpdateUrl переживал показ следующего тоста — клик по постороннему уведомлению мог открыть страницу релиза; с ярлыком это создавало бы его без спроса.
  • «Общие» — тумблер «Ярлык в меню „Пуск“» под автозапуском (SettingsActions.GetStartMenu/SetStartMenu, AppController.StartMenuEnabled/ToggleStartMenu).
  • SettingsForm.Reveal() — вывести окно вперёд без пересборки вкладок.
  • Loc RU/EN/ZH — settings.startmenu, settings.startmenu.desc, toast.firstrun.startmenu, toast.startmenu.added, toast.startmenu.failed, err.running.elsewhere.
  • Доки — CHANGELOG.md, README.md/README.ru.md (фича, вкладка «Общие», portable-раздел, абзац про ярлык и повторный запуск), CLAUDE.md (Что это, Program.cs, StartMenuShortcut/InstanceWake). ROADMAP не трогал — состав v0.19.0 правится в ветке XIC-104.

Решения и ограничения

  • UAC на клик по ярлыку остаётся (requireAdministrator). Ярлык через schtasks /run отвергнут: не будит уже запущенный экземпляр и работает только при включённом автозапуске.
  • winget uninstall ярлык не уберёт — хука на удаление у portable-пакета нет; в README сказано выключить тумблер перед удалением.

Проверка

  • dotnet build 0 ошибок / 0 предупреждений, dotnet test 923/923. Новый тест пишет и читает настоящий .lnk во временной папке — ловит сдвиг слотов vtable IShellLinkW, которого компилятор не видит; плюс тесты на правила самопочинки.
  • TM2424, вживую на portable-копии сборки: при работающей старой версии — сообщение «уже запущен»; клик по приветствию первого запуска → ярлык (цель = exe, --show, рабочая папка); ярлык при работающем приложении → вторая копия выходит, «Настройки XiControl» на переднем плане; --autostart при работающем → тишина; ярлык при закрытом → старт сразу с окном настроек; тумблер выкл → ярлык удалён.
  • Не проверено вживую: включение тумблером (тот же Set(true), что у клика по тосту), самопочинка (логика под тестами), сообщение для копии другого пользователя (код общий с путём «старая версия»).

🤖 Generated with Claude Code

Oksion and others added 2 commits October 24, 2026 21:23
…106)

Пакет winget у нас portable и ярлыков не создаёт, поэтому ярлык кладёт само
приложение — только по желанию: тумблер в Настройки → Общие или клик по
приветствию первого запуска. Свой .lnk в Start Menu\Programs профиля через
IShellLinkW, состояние — сам файл; аргумент --show сразу открывает настройки.
Самопочинка на старте по правилам автозапуска: пропавший или более старый exe —
переписать, другую папку той же версии не перехватывать.

Вторая копия раньше молча выходила, и клик по exe давал только UAC. Теперь она
сигналит сеансовое событие Local\XiControlShow (создаётся сразу за мьютексом —
ранний сигнал не теряется), делится правом на передний план, и работающая копия
открывает настройки или выводит вперёд уже открытое окно. Не нашла будильника —
копия другого пользователя или старая версия: сообщение «уже запущен».
Запуск с --autostart не будит: задача срабатывает и на возврат в сеанс.

Тосты теперь ставят действие по клику на каждом показе — иначе клик по
постороннему тосту исполнил бы чужое действие (создал бы ярлык без спроса).

Проверено вживую на portable-копии: сообщение при старой версии, ярлык по клику
на приветствие, пробуждение с окном на переднем плане, тишина на --autostart,
старт ярлыком с окном, удаление тумблером. Тест на живом .lnk ловит сдвиг слотов
vtable. Сборка 0/0, тесты 923/923.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Конфликт только в CHANGELOG [Unreleased]: обе ветки дописали «Добавлено».
Оставлены обе стороны — сначала XIC-104 из main, затем два пункта XIC-106,
«Исправлено» (XIC-103) как в main. Сборка 0/0, тесты 961/961.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Oksion
Oksion merged commit bdc4361 into main Oct 24, 2026
5 checks passed
@Oksion
Oksion deleted the feat/xic-106-start-menu-shortcut branch October 24, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant