Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0fcdc07
Improve marquee smoothness and pipe queue stability
ieproject-app May 31, 2026
4fe9030
Improve full-mode stability and timeline progress handling
ieproject-app Jun 1, 2026
50c5b14
Auto-enable Widget Music deskband after registration
ieproject-app Jun 1, 2026
a57bc9a
Implement package B title card and seek bar UX
ieproject-app Jun 1, 2026
516c1aa
Smooth full-mode title animation and reduce repaint churn
ieproject-app Jun 1, 2026
c85e6e8
Use timer queue for title card animation and refine control rendering
ieproject-app Jun 1, 2026
2d24a23
Raise seek track and reduce full-mode repaint noise
ieproject-app Jun 1, 2026
06109d9
Strengthen verifier checks for title-card timer and full-mode stability
ieproject-app Jun 1, 2026
b9adc5b
Tune marquee cadence and cap frame jumps for smoother full-mode title
ieproject-app Jun 1, 2026
53bc5b1
Make playback controls DPI-aware for sharper visuals
ieproject-app Jun 1, 2026
8ed6644
Clip paints to dirty regions for lower redraw cost
ieproject-app Jun 1, 2026
3736f4a
Limit state-update repaints to dirty regions
ieproject-app Jun 1, 2026
5e7f55e
Cache marquee text width to reduce per-frame measurement cost
ieproject-app Jun 1, 2026
e3abe7b
Reduce full-mode repaint contention for smoother marquee
ieproject-app Jun 1, 2026
a63bdab
Fix restart registration flow to re-enable taskbar band
ieproject-app Jun 1, 2026
8a490cf
Harden taskbar enable with retry and state polling
ieproject-app Jun 1, 2026
f633010
Guard taskbar re-enable flow in verification script
ieproject-app Jun 1, 2026
43568da
Add last-resort explorer retry for taskbar auto-show
ieproject-app Jun 1, 2026
fcd4dce
Handle transient tray COM failures in taskbar enable
ieproject-app Jun 1, 2026
98ff306
Sync packaged installer with taskbar recovery flow
ieproject-app Jun 1, 2026
0af16fe
Add one-command taskbar visibility diagnostic script
ieproject-app Jun 1, 2026
3a13de3
Make taskbar auto-enable non-blocking with timeout wrapper
ieproject-app Jun 1, 2026
778040b
Harden restart flow to restore explorer and session scope
ieproject-app Jun 1, 2026
4733b6f
Make taskbar auto-enable opt-in and default non-interactive
ieproject-app Jun 1, 2026
e70f129
Polish full progress layout and stabilize release
ieproject-app Jun 1, 2026
fa0137f
Add Windows installer release flow
ieproject-app Jun 1, 2026
c1f8b15
Rebrand release as SnipTune 10
ieproject-app Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* text=auto
*.cmd text eol=crlf
*.iss text eol=crlf
*.ps1 text eol=crlf
*.sln text eol=crlf
*.vcxproj text eol=crlf
*.filters text eol=crlf
*.cpp text eol=crlf
*.h text eol=crlf
*.md text eol=lf
*.txt text eol=crlf
55 changes: 55 additions & 0 deletions .github/workflows/windows-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: windows-ci

on:
push:
pull_request:

jobs:
build-test-package:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Build Debug x64
shell: cmd
run: scripts\Build.cmd Debug
- name: Run Debug tests
shell: cmd
run: scripts\Run-WidgetMusicTests.cmd Debug
- name: Build Release x64
shell: cmd
run: scripts\Build.cmd Release
- name: Run Release tests
shell: cmd
run: scripts\Run-WidgetMusicTests.cmd Release
- name: Package Release runtime
shell: cmd
run: scripts\Package-WidgetMusic.cmd Release
- name: Check runtime dependencies
shell: powershell
run: .\scripts\Check-RuntimeDependencies.ps1
- name: Verify final invariants
shell: powershell
run: .\scripts\Verify-WidgetMusicGoal.ps1 Release
- name: Build installer when Inno Setup is available
shell: powershell
run: |
$iscc = @(
"${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe",
"${env:ProgramFiles}\Inno Setup 6\ISCC.exe",
"${env:LOCALAPPDATA}\Programs\Inno Setup 6\ISCC.exe"
) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not $iscc) {
$isccCommand = Get-Command ISCC.exe -ErrorAction SilentlyContinue
if ($isccCommand) { $iscc = $isccCommand.Source }
}
if ($iscc) {
.\scripts\Build-Installer.cmd Release
} else {
Write-Host 'Inno Setup is not available on this runner; skipping installer artifact.'
}
- name: Upload installer artifact
if: ${{ hashFiles('out/dist/SnipTune10Setup-*.exe') != '' }}
uses: actions/upload-artifact@v4
with:
name: SnipTune10Setup
path: out/dist/SnipTune10Setup-*.exe
103 changes: 103 additions & 0 deletions .github/workflows/windows-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: windows-release

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
version:
description: 'Release version, for example 1.0.1'
required: true
type: string

permissions:
contents: write

jobs:
build-release:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- name: Resolve release version
shell: powershell
run: |
if ('${{ github.event_name }}' -eq 'workflow_dispatch') {
$version = '${{ inputs.version }}'
$tag = "v$version"
} else {
$tag = '${{ github.ref_name }}'
$version = $tag.TrimStart('v')
}

if ($version -notmatch '^\d+\.\d+\.\d+$') {
throw "Release version must look like 1.0.1. Got '$version'."
}

"WIDGETMUSIC_VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
"WIDGETMUSIC_TAG=$tag" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8

- name: Install Inno Setup
shell: powershell
run: choco install innosetup -y --no-progress

- name: Build installer
shell: cmd
run: scripts\Build-Installer.cmd Release

- name: Run tests
shell: cmd
run: scripts\Run-WidgetMusicTests.cmd Release

- name: Verify release invariants
shell: powershell
run: .\scripts\Verify-WidgetMusicGoal.ps1 Release

- name: Prepare release assets
shell: powershell
run: |
$version = $env:WIDGETMUSIC_VERSION
$assets = Join-Path (Get-Location) 'out\release-assets'
New-Item -ItemType Directory -Force -Path $assets | Out-Null

$installer = Join-Path (Get-Location) "out\dist\SnipTune10Setup-$version-x64.exe"
if (-not (Test-Path -LiteralPath $installer -PathType Leaf)) {
throw "Installer asset not found: $installer"
}

Copy-Item -LiteralPath $installer -Destination $assets

$runtimeZip = Join-Path $assets "SnipTune10-$version-runtime.zip"
if (Test-Path -LiteralPath $runtimeZip) {
Remove-Item -LiteralPath $runtimeZip -Force
}
Compress-Archive -Path 'out\dist\SnipTune10\*' -DestinationPath $runtimeZip -CompressionLevel Optimal

Get-ChildItem -LiteralPath $assets -File |
Sort-Object Name |
ForEach-Object {
'{0} {1}' -f (Get-FileHash -Algorithm SHA256 -LiteralPath $_.FullName).Hash.ToLowerInvariant(), $_.Name
} |
Set-Content -LiteralPath (Join-Path $assets 'SHA256SUMS.txt') -Encoding ascii

- name: Upload workflow artifacts
uses: actions/upload-artifact@v4
with:
name: SnipTune10-${{ env.WIDGETMUSIC_VERSION }}-release-assets
path: out/release-assets/*

- name: Publish GitHub release
shell: powershell
env:
GH_TOKEN: ${{ github.token }}
run: |
$assetPaths = Get-ChildItem -LiteralPath 'out\release-assets' -File |
Sort-Object Name |
ForEach-Object { $_.FullName }

gh release create $env:WIDGETMUSIC_TAG $assetPaths `
--draft `
--title "SnipTune 10 $env:WIDGETMUSIC_VERSION" `
--generate-notes `
--target $env:GITHUB_SHA
60 changes: 53 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# Widget Music (Windows 10 DeskBand + Host)
# SnipTune 10 (Windows 10 DeskBand + Host)

`Widget Music` adalah toolbar/deskband asli untuk Windows 10 yang muncul di:
`SnipTune 10` adalah toolbar/deskband asli untuk Windows 10 dari [SnipGeek](https://snipgeek.com) yang muncul di:

`Right click taskbar > Toolbars > Widget Music`
`Right click taskbar > Toolbars > SnipTune 10`

Arsitektur V1:

1. `WidgetMusicDeskband.dll` (in-proc COM DeskBand) hidup di `explorer.exe`, menggambar UI kecil di taskbar dan mengirim perintah tombol.
2. `WidgetMusicHost.exe` (out-of-proc companion) membaca/mengontrol media session via `GlobalSystemMediaTransportControlsSessionManager` dan menjadi server named pipe untuk IPC.

Komunikasi: named pipe lokal (JSON lines, UTF-8).
Komunikasi: named pipe lokal per sesi Windows (JSON lines, UTF-8) dengan handshake versi wajib.

## Build (CLI)

Prasyarat:

* Visual Studio Build Tools (Desktop development with C++) - VS 2022 atau lebih baru
* Windows 10 SDK (10.0.x)
* Inno Setup 6, hanya jika ingin membuat installer `.exe`

Build x64 Release:

Expand All @@ -35,7 +36,37 @@ Folder build Release juga berisi PDB dan intermediate file untuk debugging, jadi
.\scripts\Package-WidgetMusic.cmd Release
```

Paket kecil ada di `out\dist\WidgetMusic` dan hanya berisi DLL, EXE, serta script register/unregister.
Paket kecil ada di `out\dist\SnipTune10`. Paket membawa DLL, EXE, script runtime, `VERSION.txt`, dan `SHA256SUMS.txt`.

Untuk membuat installer Windows 10 x64:

```bat
.\scripts\Build-Installer.cmd Release
```

Output installer ada di:

`out\dist\SnipTune10Setup-1.0.1-x64.exe`

Script installer juga memeriksa agar binary Release tidak bergantung pada runtime Visual C++ dinamis seperti `MSVCP140.dll` dan `VCRUNTIME140*.dll`.

## Install dari Installer

Jalankan `SnipTune10Setup-1.0.1-x64.exe` di Windows 10 x64. Installer memasang file ke profil pengguna di `%LOCALAPPDATA%\SnipGeek\SnipTune 10`, mendaftarkan DeskBand, mencoba menampilkan toolbar otomatis, lalu me-restart Explorer sebentar agar toolbar dikenali.

Jika toolbar belum terlihat setelah install, aktifkan manual dari:

`Right click taskbar > Toolbars > SnipTune 10`

Uninstall dari Apps & Features atau Control Panel akan unregister DeskBand dan me-restart Explorer sebelum file dihapus.

## Update dari Installer

Untuk update versi berikutnya, naikkan versi aplikasi di resource/installer, build installer baru, lalu jalankan installer `.exe` baru di laptop yang sama. Karena installer memakai AppId yang sama, Inno Setup akan memperbarui instalasi yang sudah ada di `%LOCALAPPDATA%\SnipGeek\SnipTune 10`.

Saat update, installer akan unregister versi lama dan me-restart Explorer terlebih dahulu supaya `WidgetMusicDeskband.dll` tidak terkunci, menimpa file dengan versi baru, lalu register ulang dan mencoba menampilkan toolbar lagi. Untuk distribusi publik, file installer sebaiknya diberi nama sesuai versi, misalnya `SnipTune10Setup-1.0.2-x64.exe`.

Panduan update/release yang lebih lengkap ada di `docs\Panduan-Update-Release.md`. Alur GitHub Release ada di `docs\GitHub-Release-Process.md`.

## Install / Register

Expand All @@ -45,11 +76,22 @@ Register deskband + restart Explorer (direkomendasikan agar toolbar muncul):
.\scripts\Register-WidgetMusic.cmd Release restart
```

Opsional, jika ingin script mencoba menampilkan toolbar otomatis:

```bat
.\scripts\Register-WidgetMusic.cmd Release restart auto
```

Lalu aktifkan:

`Right click taskbar > Toolbars > Widget Music`
`Right click taskbar > Toolbars > SnipTune 10`

Catatan: kadang menu Toolbars perlu dibuka dua kali setelah register.
Catatan:
* Default sekarang non-interactive: script tidak auto-enable toolbar kecuali diberi flag `auto`/`enable`.
* Jika pakai mode `auto`, script memakai timeout agar proses tidak macet saat dialog konfirmasi Windows muncul.
* Restart Explorer hanya menyentuh sesi Windows pengguna yang menjalankan script.
* Jika toolbar belum terlihat, aktifkan manual dari menu Toolbars.
* Kadang menu Toolbars perlu dibuka dua kali setelah register.

## Uninstall / Unregister

Expand All @@ -64,6 +106,10 @@ Catatan: kadang menu Toolbars perlu dibuka dua kali setelah register.
* Saat toolbar dimatikan, deskband memutus pipe sehingga host ikut berhenti.
* Widget sekarang mulai dari mode compact 132x40; untuk pindah mode gunakan klik kanan pada widget lalu pilih `Compact view` atau `Full view`.
* Saat mode compact dan lagu berganti, title tampil sebentar sebagai popup native di atas widget agar lebih terbaca.
* Mode full menampilkan progress text dan progress bar display-only. Tidak ada seek melalui widget.
* Tombol bisa dioperasikan dengan keyboard: `Left`, `Right`, `Enter`, dan `Space`.
* Screen reader dapat membaca tiga tombol virtual: `Previous`, `Play/Pause`, dan `Next`.
* Tombol media hanya aktif saat ada target media yang valid; kondisi kosong tidak bisa mengirim play/pause palsu.
* Audit invariant goal bisa dijalankan dengan `.\scripts\Verify-WidgetMusicGoal.ps1 Release`.
* Test ringan bisa dijalankan dengan `.\scripts\Run-WidgetMusicTests.cmd Release`.
* Log debug, jika diaktifkan lewat registry, bisa dibaca dengan `.\scripts\Read-WidgetMusicLogs.ps1 -Tail 80`.
7 changes: 6 additions & 1 deletion WidgetMusic.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WidgetMusicDeskband", "Widg
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WidgetMusicHost", "WidgetMusicHost\\WidgetMusicHost.vcxproj", "{0C7A0AA8-9B6D-4B1D-86C5-ECBBE9C7B7AF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WidgetMusicTests", "WidgetMusicTests\\WidgetMusicTests.vcxproj", "{B811AB3A-72D8-452E-AB4A-4FA78C4E774B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand All @@ -20,9 +22,12 @@ Global
{0C7A0AA8-9B6D-4B1D-86C5-ECBBE9C7B7AF}.Debug|x64.Build.0 = Debug|x64
{0C7A0AA8-9B6D-4B1D-86C5-ECBBE9C7B7AF}.Release|x64.ActiveCfg = Release|x64
{0C7A0AA8-9B6D-4B1D-86C5-ECBBE9C7B7AF}.Release|x64.Build.0 = Release|x64
{B811AB3A-72D8-452E-AB4A-4FA78C4E774B}.Debug|x64.ActiveCfg = Debug|x64
{B811AB3A-72D8-452E-AB4A-4FA78C4E774B}.Debug|x64.Build.0 = Debug|x64
{B811AB3A-72D8-452E-AB4A-4FA78C4E774B}.Release|x64.ActiveCfg = Release|x64
{B811AB3A-72D8-452E-AB4A-4FA78C4E774B}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

33 changes: 33 additions & 0 deletions WidgetMusicDeskband/WidgetMusicDeskband.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <windows.h>

VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,1,0
PRODUCTVERSION 1,0,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "SnipGeek"
VALUE "FileDescription", "SnipTune 10 Windows 10 DeskBand"
VALUE "FileVersion", "1.0.1.0"
VALUE "InternalName", "WidgetMusicDeskband.dll"
VALUE "OriginalFilename", "WidgetMusicDeskband.dll"
VALUE "ProductName", "SnipTune 10"
VALUE "ProductVersion", "1.0.1.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1200
END
END
4 changes: 3 additions & 1 deletion WidgetMusicDeskband/WidgetMusicDeskband.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;_WINDOWS;_USRDLL;WIDGETMUSICDESKBAND_EXPORTS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
Expand All @@ -84,7 +84,9 @@

<ItemGroup>
<ClCompile Include="src\Deskband.cpp" />
<ClInclude Include="src\Accessibility.h" />
<None Include="WidgetMusicDeskband.def" />
<ResourceCompile Include="WidgetMusicDeskband.rc" />
</ItemGroup>

<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
Expand Down
15 changes: 14 additions & 1 deletion WidgetMusicDeskband/WidgetMusicDeskband.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,24 @@
<Filter Include="Source Files">
<UniqueIdentifier>{1DCC64F6-4279-4A5A-8B88-9D9E7D6E2A2B}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{E8DC70E5-94F8-4C82-9461-BEDBE18FA715}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{5D9C05D5-3BE4-4D81-B911-EE11DD47D7DD}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Deskband.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Accessibility.h">
<Filter>Header Files</Filter>
</ClInclude>
<ResourceCompile Include="WidgetMusicDeskband.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

Loading
Loading