[CRYPTE] Druide Antidote 11 & 12

Pour trouver des logiciels divers et variés
Répondre
Avatar du membre
Blik
Nouveau
Nouveau
Messages : 78
Enregistré le : il y a 11 ans

Message par Blik »

Image

Petite application pour supprimer toutes traces d'Andidote! :))
Si ça peut être utile!


Code : Tout sélectionner

TWL2.326567596864502A356373716070256460247F6D4A0D0765696034733442 3A484844763D227632714268734F632A5B41685759427B6F2275646C6F666F2A7 E6E2167656D6F2F2A33707474786
Tous les commentaires pour améliorer l’application sont les bienvenus.
Avatar du membre
LaDidi21
Modérateur
Modérateur
Messages : 14778
Enregistré le : il y a 17 ans

Message par LaDidi21 »

@Blik :
Pourquoi utilises-tu PS2EXE ?
Si c'est pour cacher, le PS1 est, en clair, dans les ressources du .NET :

Code : Tout sélectionner

<#
    LittleCleaner
    Interface WPF inspiree d'un terminal (cmd) mais en tons clairs, avec quelques couleurs
    d'accent et de petites animations. Se relance automatiquement en administrateur.
    Outils : desinstallation d'Antidote (Druide) et nettoyage des fichiers temporaires.
#>
param([switch]$FromArchive)

# ---------------------------------------------------------------------------
# Detection "lance depuis une archive" (zip/rar/7z) — DOIT se faire AVANT
# l'elevation UAC : une fois elevee, le nouveau processus est reparente par
# Windows au service AppInfo/consent.exe, ce qui casse definitivement la
# chaine de processus parents et rend impossible de remonter jusqu'a WinRAR.
# Le resultat est donc calcule ici puis transmis au processus elevé via
# le parametre -FromArchive.
# ---------------------------------------------------------------------------
function Get-ParentProcessChain([int]$procId, [int]$maxDepth = 8) {
    $names = @()
    $currentId = $procId
    for ($i = 0; $i -lt $maxDepth; $i++) {
        try { $proc = Get-CimInstance Win32_Process -Filter "ProcessId=$currentId" -ErrorAction Stop } catch { break }
        if (-not $proc) { break }
        $names += $proc.Name
        if (-not $proc.ParentProcessId -or $proc.ParentProcessId -eq 0 -or $proc.ParentProcessId -eq $currentId) { break }
        $currentId = $proc.ParentProcessId
    }
    return $names
}

function Test-RunningFromArchive {
    # Signal principal : un gestionnaire d'archives est present dans la chaine des parents
    $archiveMarkers = 'winrar', 'rar.exe', 'unrar', '7zfm', '7zg', '7z.exe', 'peazip', 'winzip', 'bandizip', 'nanazip'
    try {
        foreach ($name in (Get-ParentProcessChain -procId $PID)) {
            $lname = $name.ToLowerInvariant()
            foreach ($marker in $archiveMarkers) {
                if ($lname.Contains($marker)) { return $true }
            }
        }
    } catch { }

    # Repli : motifs de chemin habituels (dossiers temporaires d'extraction)
    if ($PSScriptRoot) {
        $p = $PSScriptRoot
        if ($p -match '\\AppData\\Local\\Temp\\' -or $p -match '(?i)^[A-Za-z]:\\Temp\\' -or $p -match '\\Windows\\Temp\\') { return $true }
        if ($p -match 'Rar\$[A-Za-z]{2}[a-z0-9]*\.[a-z0-9]+' -or $p -match '\\7z[A-Za-z0-9]+\\?$' -or $p -match '\\Temp1_.*\.(zip|rar|7z)\\') { return $true }
    }
    return $false
}

$script:ArchiveDetected = if ($FromArchive) { $true } else { Test-RunningFromArchive }

# --- Auto-elevation en administrateur (relance silencieuse) ---
$principal = New-Object Security.Principal.WindowsPrincipal(
    [Security.Principal.WindowsIdentity]::GetCurrent())
$IsAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $IsAdmin) {
    try {
        $relaunchArgs = @(
            '-NoProfile', '-WindowStyle', 'Hidden', '-ExecutionPolicy', 'Bypass',
            '-File', "`"$PSCommandPath`""
        )
        if ($script:ArchiveDetected) { $relaunchArgs += '-FromArchive' }
        Start-Process powershell.exe -Verb RunAs -WindowStyle Hidden -ArgumentList $relaunchArgs
    } catch { }
    exit
}

Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase

# ---------------------------------------------------------------------------
# Definition des outils et de leurs etapes
# ---------------------------------------------------------------------------
$Steps = @(
    [pscustomobject]@{ Group = 'Fermer les applications'; Name = 'Antidote'; Detail = 'Processus Antidote.exe'
        Action = { Stop-Process -Name 'Antidote' -Force -ErrorAction Stop }
        Check  = { [bool](Get-Process -Name 'Antidote' -ErrorAction SilentlyContinue) } }
    [pscustomobject]@{ Group = 'Fermer les applications'; Name = 'Service Connectix'; Detail = 'Processus ServiceConnectixAntidote.exe'
        Action = {
            # Si c'est un vrai service Windows, l'arreter proprement via le gestionnaire
            # de services evite qu'il ne se relance tout seul (comportement typique des
            # services avec redemarrage automatique) - Stop-Process seul ne suffit pas.
            $svc = Get-Service -Name 'ServiceConnectixAntidote' -ErrorAction SilentlyContinue
            if ($svc) { Stop-Service -Name 'ServiceConnectixAntidote' -Force -ErrorAction Stop }
            Stop-Process -Name 'ServiceConnectixAntidote' -Force -ErrorAction SilentlyContinue
        }
        Check  = { [bool](Get-Process -Name 'ServiceConnectixAntidote' -ErrorAction SilentlyContinue) } }
    [pscustomobject]@{ Group = 'Fermer les applications'; Name = 'Connectix'; Detail = 'Processus Connectix.exe'
        Action = { Stop-Process -Name 'Connectix' -Force -ErrorAction Stop }
        Check  = { [bool](Get-Process -Name 'Connectix' -ErrorAction SilentlyContinue) } }
    [pscustomobject]@{ Group = 'Fermer les applications'; Name = 'OutilsIntegration'; Detail = 'Processus OutilsIntegration.exe'
        Action = { Stop-Process -Name 'OutilsIntegration' -Force -ErrorAction Stop }
        Check  = { [bool](Get-Process -Name 'OutilsIntegration' -ErrorAction SilentlyContinue) } }

    [pscustomobject]@{ Group = 'Fichiers'; Name = 'A4738S1I2.bin'; Detail = 'C:\Windows\A4738S1I2.bin'
        Action = { Remove-Item 'C:\Windows\A4738S1I2.bin' -Force -ErrorAction Stop }
        Check  = { Test-Path 'C:\Windows\A4738S1I2.bin' } }
    [pscustomobject]@{ Group = 'Fichiers'; Name = 'WSU371TA.bin'; Detail = 'C:\Windows\system32\WSU371TA.bin'
        Action = { Remove-Item 'C:\Windows\system32\WSU371TA.bin' -Force -ErrorAction Stop }
        Check  = { Test-Path 'C:\Windows\system32\WSU371TA.bin' } }

    [pscustomobject]@{ Group = 'Dossiers'; Name = 'Druide (AppData)'; Detail = '%AppData%\Roaming\Druide (tous les profils)'
        Action = {
            Get-ChildItem 'C:\Users' -Directory -ErrorAction SilentlyContinue | ForEach-Object {
                $p = Join-Path $_.FullName 'AppData\Roaming\Druide'
                if (Test-Path $p) { Remove-Item $p -Recurse -Force -ErrorAction Stop }
            }
        }
        Check  = { [bool](Get-ChildItem 'C:\Users' -Directory -ErrorAction SilentlyContinue |
                    Where-Object { Test-Path (Join-Path $_.FullName 'AppData\Roaming\Druide') }) } }
    [pscustomobject]@{ Group = 'Dossiers'; Name = 'Druide (x86)'; Detail = 'C:\Program Files (x86)\Druide'
        Action = { Remove-Item 'C:\Program Files (x86)\Druide' -Recurse -Force -ErrorAction Stop }
        Check  = { Test-Path 'C:\Program Files (x86)\Druide' } }
    [pscustomobject]@{ Group = 'Dossiers'; Name = 'Druide (x64)'; Detail = 'C:\Program Files\Druide'
        Action = { Remove-Item 'C:\Program Files\Druide' -Recurse -Force -ErrorAction Stop }
        Check  = { Test-Path 'C:\Program Files\Druide' } }
    [pscustomobject]@{ Group = 'Dossiers'; Name = 'Menu Démarrer'; Detail = 'C:\ProgramData\...\Start Menu\Programs\Antidote'
        Action = { Remove-Item 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Antidote' -Recurse -Force -ErrorAction Stop }
        Check  = { Test-Path 'C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Antidote' } }

    [pscustomobject]@{ Group = 'Registre'; Name = 'HKCU'; Detail = 'HKCU:\Software\Druide informatique inc.'
        Action = { Remove-Item 'HKCU:\Software\Druide informatique inc.' -Recurse -Force -ErrorAction Stop }
        Check  = { Test-Path 'HKCU:\Software\Druide informatique inc.' } }
    [pscustomobject]@{ Group = 'Registre'; Name = 'HKLM'; Detail = 'HKLM:\SOFTWARE\Druide informatique inc.'
        Action = { Remove-Item 'HKLM:\SOFTWARE\Druide informatique inc.' -Recurse -Force -ErrorAction Stop }
        Check  = { Test-Path 'HKLM:\SOFTWARE\Druide informatique inc.' } }
)

$StepsTemp = @(
    [pscustomobject]@{ Group = 'Fichiers temporaires'; Name = 'Temp utilisateur'; Detail = "$env:TEMP"
        Action = { Get-ChildItem $env:TEMP -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue }
        Check  = { @(Get-ChildItem $env:TEMP -Force -ErrorAction SilentlyContinue).Count } }
    [pscustomobject]@{ Group = 'Fichiers temporaires'; Name = 'Temp Windows'; Detail = 'C:\Windows\Temp'
        Action = { Get-ChildItem 'C:\Windows\Temp' -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue }
        Check  = { @(Get-ChildItem 'C:\Windows\Temp' -Force -ErrorAction SilentlyContinue).Count } }
    [pscustomobject]@{ Group = 'Fichiers temporaires'; Name = 'Prefetch'; Detail = 'C:\Windows\Prefetch\*.pf'
        Action = { Get-ChildItem 'C:\Windows\Prefetch' -Filter *.pf -Force -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue }
        Check  = { @(Get-ChildItem 'C:\Windows\Prefetch' -Filter *.pf -Force -ErrorAction SilentlyContinue).Count } }
)

$Tools = @(
    [pscustomobject]@{ Title = "Désinstallation d'Antidote 12";     Menu = "[1] Désinstallation d'Antidote 12";     Sub = 'Supprimer Antidote 12 et ses traces';        Steps = $Steps }
    [pscustomobject]@{ Title = 'Vider les fichiers temporaires'; Menu = '[2] Vider les fichiers temporaires'; Sub = 'Temp utilisateur, Temp Windows, Prefetch'; Steps = $StepsTemp }
)

# ---------------------------------------------------------------------------
# Option cachee (easter egg) : n'apparait dans aucune carte du menu, accessible
# uniquement via le raccourci clavier secret "3". Rien n'est reellement modifie
# sur le systeme — c'est juste une petite blague de sysadmin.
# ---------------------------------------------------------------------------
$script:jokeDone1 = $false
$script:jokeDone2 = $false
$script:jokeDone3 = $false
$StepsJoke = @(
    [pscustomobject]@{ Group = 'Diagnostic'; Name = 'Café'; Detail = 'Niveau dans la tasse : critique'
        Check  = { -not $script:jokeDone1 }
        Action = { Start-Sleep -Milliseconds 300; $script:jokeDone1 = $true } }
    [pscustomobject]@{ Group = 'Diagnostic'; Name = 'Motivation'; Detail = 'C:\Users\Toi\Motivation\lundi.txt'
        Check  = { -not $script:jokeDone2 }
        Action = { Start-Sleep -Milliseconds 300; $script:jokeDone2 = $true } }
    [pscustomobject]@{ Group = 'Diagnostic'; Name = 'Bug'; Detail = 'Trouve entre la chaise et le clavier'
        Check  = { -not $script:jokeDone3 }
        Action = { Start-Sleep -Milliseconds 300; $script:jokeDone3 = $true } }
)
$HiddenTool = [pscustomobject]@{ Title = 'Diagnostic PEBCAK'; Steps = $StepsJoke }

# ---------------------------------------------------------------------------
# Interface — terminal clair (palette type "Solarized Light")
# ---------------------------------------------------------------------------
[xml]$xaml = @'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="LittleCleaner" Height="420" MinHeight="312" Width="552"
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize" WindowStyle="None"
        Background="#EEE8D5" FontFamily="Consolas, Lucida Console, Courier New">
    <Window.Resources>

        <Style x:Key="Primary" TargetType="Button">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Foreground" Value="#FDF6E3"/>
            <Setter Property="Background" Value="#268BD2"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Padding" Value="14,5"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True"><Setter Property="Opacity" Value="0.85"/></Trigger>
                            <Trigger Property="IsEnabled" Value="False"><Setter Property="Opacity" Value="0.40"/></Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="Secondary" TargetType="Button" BasedOn="{StaticResource Primary}">
            <Setter Property="Foreground" Value="#657B83"/>
            <Setter Property="Background" Value="#FDF6E3"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}" BorderBrush="#D9D2BC" BorderThickness="1"
                                CornerRadius="4" Padding="{TemplateBinding Padding}">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True"><Setter Property="Opacity" Value="0.75"/></Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="Ghost" TargetType="Button" BasedOn="{StaticResource Primary}">
            <Setter Property="Foreground" Value="#93A1A1"/>
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="FontSize" Value="12"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border x:Name="b" Background="{TemplateBinding Background}" CornerRadius="4" Padding="{TemplateBinding Padding}">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True"><Setter TargetName="b" Property="Background" Value="#E6DFC8"/></Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="Card" TargetType="Button" BasedOn="{StaticResource Primary}">
            <Setter Property="Foreground" Value="#073642"/>
            <Setter Property="Background" Value="#FDF6E3"/>
            <Setter Property="Padding" Value="12"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border x:Name="b" Background="{TemplateBinding Background}" BorderBrush="#D9D2BC" BorderThickness="1"
                                CornerRadius="5" Padding="{TemplateBinding Padding}" ClipToBounds="True">
                            <ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True"><Setter TargetName="b" Property="BorderBrush" Value="#268BD2"/></Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </Window.Resources>

  <Border x:Name="RootCard" Margin="12" Background="#FDF6E3" BorderBrush="#D9D2BC" BorderThickness="1" CornerRadius="6">
   <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!-- Barre de titre : chrome de terminal, en tons clairs -->
    <Border x:Name="TitleBar" Grid.Row="0" Background="#F5EFDC" CornerRadius="6,6,0,0" Padding="12,6" BorderBrush="#D9D2BC" BorderThickness="0,0,0,1">
        <Grid>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                <Ellipse Width="10" Height="10" Fill="#DC322F" Margin="0,0,6,0"/>
                <Ellipse Width="10" Height="10" Fill="#B58900" Margin="0,0,6,0"/>
                <Ellipse Width="10" Height="10" Fill="#859900" Margin="0,0,6,0"/>
                <TextBlock Text="littlecleaner" Foreground="#93A1A1" FontSize="11" Margin="12,0,0,0" VerticalAlignment="Center"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                <Button x:Name="BtnMin" Content="&#x2013;" Style="{StaticResource Ghost}" Width="26" Height="20"/>
                <Button x:Name="BtnClose" Content="&#x2715;" Style="{StaticResource Ghost}" Width="26" Height="20" Margin="4,0,0,0" FontSize="11"/>
            </StackPanel>
        </Grid>
    </Border>

    <!-- Titre unique et persistant : ne fait jamais partie du fondu, se contente de
         changer de texte. La fleche retour reserve toujours son espace (Hidden plutot
         que Collapsed) pour que le titre ne se decale jamais horizontalement. -->
    <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="20,16,20,0">
        <Button x:Name="BtnBack" Style="{StaticResource Secondary}"
                FontSize="13" Width="30" Height="30" Padding="0" VerticalAlignment="Center" Margin="0,0,10,0">
            <Grid x:Name="BackGlyphHost" RenderTransformOrigin="0.5,0.5">
                <Grid.RenderTransform>
                    <ScaleTransform x:Name="HeartScale" ScaleX="1" ScaleY="1"/>
                </Grid.RenderTransform>
                <TextBlock x:Name="HeartIcon" Text="&#x2764;" Foreground="#D33682"
                           FontFamily="Segoe UI Symbol, Segoe UI, Consolas" FontSize="14"
                           HorizontalAlignment="Center" VerticalAlignment="Center"/>
                <Viewbox x:Name="ArrowGlyph" Width="14" Height="14" Visibility="Collapsed">
                    <Canvas Width="24" Height="24">
                        <Polyline Points="16,4 8,12 16,20"
                                  Stroke="#657B83" StrokeThickness="4.2"
                                  StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round"
                                  Fill="Transparent"/>
                    </Canvas>
                </Viewbox>
            </Grid>
        </Button>
        <TextBlock x:Name="PanelTitle" Text="LittleCleaner" Foreground="#268BD2" FontSize="18" FontWeight="Bold" VerticalAlignment="Center"/>
    </StackPanel>

    <Grid Grid.Row="2">

      <!-- MENU -->
      <Grid x:Name="MenuPanel" Margin="20,10,20,20">
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <StackPanel x:Name="MenuHeader" Grid.Row="0">
              <TextBlock x:Name="MenuSubtitle" Foreground="#93A1A1" FontSize="11" Text=""/>
              <Border Height="1" Background="#D9D2BC" Margin="0,9,0,0"/>
          </StackPanel>
          <StackPanel x:Name="MenuButtons" Grid.Row="1" Margin="0,10,0,0">
              <Button x:Name="BtnTool1" Style="{StaticResource Card}" HorizontalAlignment="Stretch" Margin="0,0,0,7">
                  <Grid>
                      <Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
                      <StackPanel Grid.Column="0">
                          <TextBlock x:Name="Tool1Menu" FontSize="13" FontWeight="Bold" Foreground="#073642"/>
                          <TextBlock x:Name="Tool1Sub" FontSize="10" Foreground="#93A1A1" Margin="0,3,0,0"/>
                      </StackPanel>
                      <Border x:Name="Badge1" Grid.Column="1" Background="#93A1A1" CornerRadius="9" Padding="6,2" VerticalAlignment="Center" Visibility="Collapsed">
                          <TextBlock x:Name="Badge1Text" Foreground="#FDF6E3" FontWeight="Bold" FontSize="11"/>
                      </Border>
                  </Grid>
              </Button>
              <Button x:Name="BtnTool2" Style="{StaticResource Card}" HorizontalAlignment="Stretch">
                  <Grid>
                      <Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions>
                      <StackPanel Grid.Column="0">
                          <TextBlock x:Name="Tool2Menu" FontSize="13" FontWeight="Bold" Foreground="#073642"/>
                          <TextBlock x:Name="Tool2Sub" FontSize="10" Foreground="#93A1A1" Margin="0,3,0,0"/>
                      </StackPanel>
                      <Border x:Name="Badge2" Grid.Column="1" Background="#93A1A1" CornerRadius="9" Padding="6,2" VerticalAlignment="Center" Visibility="Collapsed">
                          <TextBlock x:Name="Badge2Text" Foreground="#FDF6E3" FontWeight="Bold" FontSize="11"/>
                      </Border>
                  </Grid>
              </Button>
              <TextBlock x:Name="AdminTextMenu" FontSize="10" Margin="2,9,0,0" Visibility="Collapsed"/>
              <Border x:Name="ArchiveWarning" Background="#FBEEC9" BorderBrush="#B58900" BorderThickness="1"
                      CornerRadius="5" Padding="10,8" Margin="0,10,0,0" Visibility="Collapsed">
                  <TextBlock TextWrapping="Wrap" FontSize="10" Foreground="#7A5A00"
                             Text="Cette application semble être lancée depuis un fichier compressé (zip/rar). Pour une meilleure utilisation, extrais d'abord le fichier puis relance-la depuis son emplacement extrait."/>
              </Border>
          </StackPanel>
          <TextBlock x:Name="VersionText" Grid.Row="2" Text="Blik - v2.0.0.2" Foreground="#B3AD9C" FontSize="9" HorizontalAlignment="Right" Margin="0,10,0,0"/>
      </Grid>

      <!-- TACHE -->
      <Grid x:Name="TaskPanel" Margin="16,10,16,16" Visibility="Collapsed">
          <Grid.RowDefinitions>
              <RowDefinition Height="*"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <StackPanel x:Name="TaskCenterRow" Grid.Row="0" VerticalAlignment="Center">
              <TextBlock x:Name="AdminText" FontSize="10" Margin="0,9,0,0" Visibility="Collapsed"/>
              <ScrollViewer Margin="0,22,0,0" VerticalScrollBarVisibility="Auto">
                  <ItemsControl x:Name="StepList"/>
              </ScrollViewer>
              <StackPanel Orientation="Horizontal" Margin="0,6,0,0">
                  <TextBlock x:Name="StatusText" Foreground="#586E75" FontSize="11"/>
                  <TextBlock x:Name="Cursor" Text="&#x2588;" Foreground="#268BD2" FontSize="11" Margin="3,0,0,0"/>
              </StackPanel>
          </StackPanel>
          <StackPanel x:Name="TaskButtonsRow" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,18,0,0">
              <Button x:Name="BtnCancel" Content="[Esc] Revenir" Style="{StaticResource Secondary}" Margin="0,0,10,0"/>
              <Button x:Name="BtnRun" Content="[Enter] Exécuter" Style="{StaticResource Primary}"/>
          </StackPanel>
      </Grid>

    </Grid>
   </Grid>
  </Border>
</Window>
'@

$win = [Windows.Markup.XamlReader]::Load((New-Object System.Xml.XmlNodeReader $xaml))

# Raccourcis vers les elements
$stepList   = $win.FindName('StepList')
$statusText = $win.FindName('StatusText')
$cursor     = $win.FindName('Cursor')
$btnRun     = $win.FindName('BtnRun')
$menuPanel  = $win.FindName('MenuPanel')
$taskPanel  = $win.FindName('TaskPanel')
$panelTitle = $win.FindName('PanelTitle')
$menuSub    = $win.FindName('MenuSubtitle')
$rootCard   = $win.FindName('RootCard')
$badge1     = $win.FindName('Badge1');     $badge1Text = $win.FindName('Badge1Text')
$badge2     = $win.FindName('Badge2');     $badge2Text = $win.FindName('Badge2Text')
$btnTool1   = $win.FindName('BtnTool1')
$btnTool2   = $win.FindName('BtnTool2')
$menuHeader  = $win.FindName('MenuHeader')
$menuButtons = $win.FindName('MenuButtons')
$versionText = $win.FindName('VersionText')
$btnBack       = $win.FindName('BtnBack')
$backGlyphHost = $win.FindName('BackGlyphHost')
$heartIcon     = $win.FindName('HeartIcon')
$arrowGlyph    = $win.FindName('ArrowGlyph')
$heartScale    = $win.FindName('HeartScale')
$taskCenterRow = $win.FindName('TaskCenterRow')
$taskButtonsRow = $win.FindName('TaskButtonsRow')

# Barre de titre (fenetre sans bordure)
$win.FindName('TitleBar').Add_MouseLeftButtonDown({ $win.DragMove() })
$win.FindName('BtnClose').Add_Click({ $win.Close() })
$win.FindName('BtnMin').Add_Click({ $win.WindowState = 'Minimized' })
$win.FindName('BtnCancel').Add_Click({ Go-Back })

# Curseur clignotant (petite animation, comme un vrai prompt)
$cursorTimer = New-Object Windows.Threading.DispatcherTimer
$cursorTimer.Interval = [timespan]::FromMilliseconds(500)
$cursorTimer.Add_Tick({ $cursor.Visibility = if ($cursor.Visibility -eq 'Visible') { 'Hidden' } else { 'Visible' } })
$cursorTimer.Start()
$win.Add_Closed({ $cursorTimer.Stop() })

# Confirmation avant fermeture si un nettoyage est en cours
$script:cleanupInProgress = $false
$win.Add_Closing({
    param($s, $e)
    if ($script:cleanupInProgress) {
        $reponse = [System.Windows.MessageBox]::Show(
            "Un nettoyage est en cours. Fermer maintenant peut interrompre des suppressions en plein milieu.`n`nFermer quand même ?",
            'LittleCleaner', 'YesNo', 'Warning')
        if ($reponse -ne 'Yes') { $e.Cancel = $true }
    }
})

# Libelles des outils
$win.FindName('Tool1Menu').Text = $Tools[0].Menu; $win.FindName('Tool1Sub').Text = $Tools[0].Sub
$win.FindName('Tool2Menu').Text = $Tools[1].Menu; $win.FindName('Tool2Sub').Text = $Tools[1].Sub

# Mention admin (seulement si NON admin)
if (-not $IsAdmin) {
    foreach ($n in 'AdminText', 'AdminTextMenu') {
        $t = $win.FindName($n); $t.Text = '[!] Mode administrateur inactif'; $t.Foreground = '#DC322F'; $t.Visibility = 'Visible'
    }
}

# Avertissement si l'app a ete lancee depuis une archive (detecte plus haut,
# avant l'elevation, pour garder une chaine de processus parents exploitable)
if ($script:ArchiveDetected) {
    $win.FindName('ArchiveWarning').Visibility = 'Visible'
}

# ---------------------------------------------------------------------------
# Palette (type Solarized Light) et helpers
# ---------------------------------------------------------------------------
$COL_TEXT  = '#073642'   # texte principal (fonce)
$COL_MUTED = '#93A1A1'   # texte secondaire
$COL_FAINT = '#CBC5AE'   # texte eteint
$COL_OK    = '#859900'   # succes (vert olive)
$COL_KO    = '#DC322F'   # echec (rouge)
$COL_PART  = '#B58900'   # partiel (or/ambre)
$COL_SPIN  = '#CB4B16'   # progression (orange)
$COL_PRES  = '#268BD2'   # cible presente (bleu)

$SYM_WAIT = [char]0x00B7   # .
$SYM_PRES = [char]0x25CF   # ● present
$SYM_ABS  = [char]0x25CB   # ○ absent
$SYM_OK   = [char]0x2714   # ✔
$SYM_KO   = [char]0x2716   # ✖
$SYM_PART = [char]0x25D1   # ◑ partiel
$SPIN     = '/', '-', '\', '|'

# Attente non bloquante (garde l'UI reactive)
function Wait-Ui([int]$ms) {
    $end = [datetime]::Now.AddMilliseconds($ms)
    while ([datetime]::Now -lt $end) {
        $win.Dispatcher.Invoke([action] {}, 'Background')
        Start-Sleep -Milliseconds 25
    }
}

# Fondu d'apparition (utilise pour le resultat ET pour chaque ligne construite)
function Flash-Text($el, [int]$ms = 150) {
    $a = New-Object Windows.Media.Animation.DoubleAnimation
    $a.From = 0.1; $a.To = 1.0
    $a.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds($ms))
    $el.BeginAnimation([Windows.UIElement]::OpacityProperty, $a)
}

# Mesure la hauteur naturelle du contenu actuellement affiche, SANS toucher a la
# taille reelle de la fenetre (passe de mesure "a blanc" — evite tout saut visuel).
function Get-DesiredWindowHeight {
    $win.UpdateLayout()   # force la mise a jour complete du visuel avant de mesurer
    $avail = New-Object Windows.Size($win.ActualWidth, [double]::PositiveInfinity)
    $rootCard.Measure($avail)
    return $rootCard.DesiredSize.Height
}

# Anime en douceur la hauteur de la fenetre vers sa taille de contenu actuelle
# (agrandissement ou retrecissement selon le cas).
function Resize-WindowToContent([int]$ms = 440) {
    $target = Get-DesiredWindowHeight
    if ($target -le 0 -or [math]::Abs($win.ActualHeight - $target) -lt 1) { return }
    $a = New-Object Windows.Media.Animation.DoubleAnimation
    $a.From = $win.ActualHeight; $a.To = $target
    $a.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds($ms))
    $ease = New-Object Windows.Media.Animation.QuadraticEase; $ease.EasingMode = 'EaseInOut'
    $a.EasingFunction = $ease
    $win.BeginAnimation([Windows.Window]::HeightProperty, $a)
}

# Anime l'opacite d'un element (fondu simple, duree fixe)
function Fade-Opacity($el, [double]$to, [int]$ms) {
    $a = New-Object Windows.Media.Animation.DoubleAnimation
    $a.To = $to
    $a.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds($ms))
    $el.BeginAnimation([Windows.UIElement]::OpacityProperty, $a)
}

# Fait disparaitre une serie d'elements en cascade, dans l'ordre fourni (typiquement
# du bas vers le haut de l'ecran) : chaque fondu demarre un court instant apres le
# precedent, ce qui cree un effet de vague plutot qu'une disparition simultanee.
function Fade-OutCascade($elements, [int]$fadeMs = 200, [int]$staggerMs = 90) {
    foreach ($el in $elements) {
        Fade-Opacity $el 0 $fadeMs
        Wait-Ui $staggerMs
    }
    Wait-Ui ([math]::Max(0, $fadeMs - $staggerMs))   # laisse la derniere animation se terminer
}

# Remet l'opacite d'une serie d'elements a 1 (ex. apres les avoir fait disparaitre
# et masque le panneau — pour qu'ils soient prets a etre revus normalement).
function Reset-Opacity($elements) {
    foreach ($el in $elements) {
        $el.BeginAnimation([Windows.UIElement]::OpacityProperty, $null)
        $el.Opacity = 1
    }
}

# Petit battement de coeur (grossit puis reprend sa taille) au clic, purement decoratif
function Animate-HeartBeat {
    $up = New-Object Windows.Media.Animation.DoubleAnimation
    $up.To = 1.5
    $up.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds(120))
    $easeUp = New-Object Windows.Media.Animation.BackEase; $easeUp.EasingMode = 'EaseOut'; $easeUp.Amplitude = 0.4
    $up.EasingFunction = $easeUp
    $up.Add_Completed({
        $down = New-Object Windows.Media.Animation.DoubleAnimation
        $down.To = 1.0
        $down.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds(180))
        $easeDown = New-Object Windows.Media.Animation.BackEase; $easeDown.EasingMode = 'EaseOut'; $easeDown.Amplitude = 0.3
        $down.EasingFunction = $easeDown
        $heartScale.BeginAnimation([Windows.Media.ScaleTransform]::ScaleXProperty, $down)
        $heartScale.BeginAnimation([Windows.Media.ScaleTransform]::ScaleYProperty, $down)
    })
    $heartScale.BeginAnimation([Windows.Media.ScaleTransform]::ScaleXProperty, $up)
    $heartScale.BeginAnimation([Windows.Media.ScaleTransform]::ScaleYProperty, $up)
}

# Effet machine a ecrire : revele le texte caractere par caractere
function Type-Text($tb, [string]$text, [int]$ms = 14, [int]$gen = -1) {
    $tb.Text = ''
    foreach ($ch in $text.ToCharArray()) {
        if ($gen -ge 0 -and $script:scanGeneration -ne $gen) { return }
        $tb.Text += $ch
        Wait-Ui $ms
    }
}

# Grise entierement une ligne (nom + chemin) vers le texte eteint
function Dim-Line($r) {
    foreach ($tb in $r.Name, $r.Path) {
        $b = $tb.Foreground.Clone(); $tb.Foreground = $b
        $anim = New-Object Windows.Media.Animation.ColorAnimation
        $anim.To = [Windows.Media.ColorConverter]::ConvertFromString($COL_FAINT)
        $anim.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds(700))
        $b.BeginAnimation([Windows.Media.SolidColorBrush]::ColorProperty, $anim)
    }
}

# Rallume une ligne
function Light-Line($r) {
    $r.Name.Foreground = $COL_TEXT
    $r.Path.Foreground = $COL_MUTED
}

# Remet toutes les lignes de l'outil actif a l'etat neutre ("en attente"), pour
# ne jamais montrer un vieux resultat (scan ou execution precedente) au moment
# ou la page redevient visible.
function Reset-RowsToWaiting {
    foreach ($r in $script:rows.Values) {
        $r.Status.Text = $SYM_WAIT
        $r.Status.Foreground = $COL_FAINT
        $r.Border.ToolTip = $null
        Light-Line $r
    }
}

function Spin-Status($r, $cycles, $ms, [int]$gen = -1) {
    $r.Status.Foreground = $COL_SPIN
    for ($i = 0; $i -lt $cycles; $i++) {
        if ($gen -ge 0 -and $script:scanGeneration -ne $gen) { return }
        $r.Status.Text = $SPIN[$i % 4]; Wait-Ui $ms
    }
}

# ---------------------------------------------------------------------------
# Construction des lignes (avec cache : un outil deja visite n'est pas reconstruit)
# ---------------------------------------------------------------------------
$script:rowCache = @{}
$script:scanGeneration = 0   # jeton d'annulation : incremente a chaque nouveau scan / retour arriere

function Build-Steps($steps) {
    $stepList.Items.Clear()
    $script:rows = @{}
    $lastGroup = $null
    foreach ($s in $steps) {
        if ($s.Group -ne $lastGroup) {
            $hdr = New-Object Windows.Controls.TextBlock
            $hdr.Text = "-- $($s.Group) --".ToUpper()
            $hdr.Foreground = '#B58900'; $hdr.FontSize = 10; $hdr.FontWeight = 'Bold'
            $top = if ($null -eq $lastGroup) { 0 } else { 7 }
            $hdr.Margin = "0,$top,0,3"
            $stepList.Items.Add($hdr) | Out-Null
            $lastGroup = $s.Group
        }

        $border = New-Object Windows.Controls.Border
        $border.Background = '#FDF6E3'; $border.BorderBrush = '#E3DCC5'; $border.BorderThickness = 1
        $border.Padding = '6,3'; $border.Margin = '0,0,0,2'

        $grid = New-Object Windows.Controls.Grid
        foreach ($wdt in '160', '*', '30') {
            $c = New-Object Windows.Controls.ColumnDefinition; $c.Width = $wdt; $grid.ColumnDefinitions.Add($c)
        }

        $t1 = New-Object Windows.Controls.TextBlock
        $t1.Text = "> $($s.Name)"; $t1.Foreground = $COL_TEXT
        $t1.FontWeight = 'Bold'; $t1.FontSize = 11; $t1.VerticalAlignment = 'Center'
        $t1.TextTrimming = 'CharacterEllipsis'
        [Windows.Controls.Grid]::SetColumn($t1, 0)

        $t2 = New-Object Windows.Controls.TextBlock
        $t2.Text = $s.Detail; $t2.Foreground = $COL_MUTED
        $t2.FontSize = 10; $t2.VerticalAlignment = 'Center'; $t2.TextTrimming = 'CharacterEllipsis'
        [Windows.Controls.Grid]::SetColumn($t2, 1)

        $st = New-Object Windows.Controls.TextBlock
        $st.Text = $SYM_WAIT; $st.Foreground = $COL_FAINT; $st.FontSize = 13
        $st.VerticalAlignment = 'Center'; $st.HorizontalAlignment = 'Center'
        [Windows.Controls.Grid]::SetColumn($st, 2)

        $grid.Children.Add($t1); $grid.Children.Add($t2); $grid.Children.Add($st)
        $border.Child = $grid
        $stepList.Items.Add($border) | Out-Null
        $script:rows[$s.Name] = @{ Name = $t1; Path = $t2; Status = $st; Border = $border }
    }
}

# Affiche les lignes d'un outil : les reconstruit une seule fois, puis reutilise
# les elements deja crees (cache) pour eviter de refaire tout le layout a chaque entree.
function Show-Steps($tool) {
    $key = $tool.Title
    if ($script:rowCache.ContainsKey($key)) {
        $cache = $script:rowCache[$key]
        $stepList.Items.Clear()
        foreach ($item in $cache.Items) { $stepList.Items.Add($item) | Out-Null }
        $script:rows = $cache.Rows
        return
    }
    Build-Steps $tool.Steps
    $snapshot = @()
    foreach ($it in $stepList.Items) { $snapshot += $it }
    $script:rowCache[$key] = @{ Items = $snapshot; Rows = $script:rows }
}

# Invalide le cache d'un outil (ex. apres une execution qui a change son etat)
function Clear-StepsCache($title) {
    if ($script:rowCache.ContainsKey($title)) { $script:rowCache.Remove($title) }
}

# Detection presence : ● present (bleu) / ○ absent (ligne eteinte)
# Appelee uniquement par Enter-Tool, qui a deja desactive $btnRun avant d'appeler.
function Run-Detection($myGen) {
    Type-Text $statusText 'C:\> analyse des cibles...' 12 $myGen
    if ($script:scanGeneration -ne $myGen) { return }   # annule (retour arriere ou nouveau scan entre-temps)
    $anyPresent = $false
    foreach ($s in $script:activeSteps) {
        if ($script:scanGeneration -ne $myGen) { return }
        $r = $script:rows[$s.Name]
        Light-Line $r
        $r.Border.ToolTip = $null
        Spin-Status $r 6 26 $myGen
        if ($script:scanGeneration -ne $myGen) { return }
        if (& $s.Check) {
            $r.Status.Text = $SYM_PRES; $r.Status.Foreground = $COL_PRES
            $anyPresent = $true
        } else {
            $r.Status.Text = $SYM_ABS; $r.Status.Foreground = $COL_MUTED; Dim-Line $r
        }
        Flash-Text $r.Status
    }
    if ($script:scanGeneration -ne $myGen) { return }
    if ($anyPresent) {
        Type-Text $statusText 'C:\> prêt.' 12 $myGen
        $btnRun.IsEnabled = $true
    } else {
        Type-Text $statusText 'C:\> déjà nettoyé, rien à faire.' 12 $myGen
        $btnRun.IsEnabled = $false
    }
}

function Enter-Tool($tool) {
    $script:scanGeneration++
    $myGen = $script:scanGeneration
    $script:activeTool  = $tool
    $script:activeSteps = $tool.Steps

    # Empeche tout double-clic pendant la transition
    $btnTool1.IsEnabled = $false; $btnTool2.IsEnabled = $false

    # Le menu disparait en cascade, du bas vers le haut
    Fade-OutCascade @($versionText, $menuButtons, $menuHeader)
    if ($script:scanGeneration -ne $myGen) { return }

    $menuPanel.Visibility = 'Collapsed'
    Reset-Opacity @($versionText, $menuButtons, $menuHeader)

    # Le glyphe (coeur -> fleche) et le titre disparaissent puis reapparaissent ensemble,
    # sans jamais toucher au "carre" du bouton lui-meme (qui reste toujours plein).
    Fade-Opacity $backGlyphHost 0 150; Fade-Opacity $panelTitle 0 150
    Wait-Ui 150
    $heartIcon.Visibility = 'Collapsed'; $arrowGlyph.Visibility = 'Visible'
    $panelTitle.Text = $tool.Title
    Fade-Opacity $backGlyphHost 1 150; Fade-Opacity $panelTitle 1 150

    $taskPanel.Opacity = 0; $taskPanel.Visibility = 'Visible'
    $btnRun.IsEnabled = $false   # jamais activable avant la fin complete du scan
    $btnRun.Content = '[Enter] Exécuter'
    Show-Steps $tool   # remplace la liste tout de suite
    Reset-RowsToWaiting   # jamais d'ancien resultat visible avant que le nouveau scan ne demarre
    Resize-WindowToContent
    Fade-Opacity $taskPanel 1 250

    Run-Detection $myGen
}

$win.FindName('BtnTool1').Add_Click({ Enter-Tool $Tools[0] })
$win.FindName('BtnTool2').Add_Click({ Enter-Tool $Tools[1] })

function Go-Back {
    if ($taskPanel.Visibility -eq 'Visible') {
        if ($script:cleanupInProgress) {
            $reponse = [System.Windows.MessageBox]::Show(
                "Un nettoyage est en cours. Revenir en arrière n'interrompra pas les suppressions, mais tu ne verras plus leur progression.`n`nRevenir quand même ?",
                'LittleCleaner', 'YesNo', 'Warning')
            if ($reponse -ne 'Yes') { return }
        }
        $script:scanGeneration++   # annule immediatement tout scan en cours

        # La page tache disparait en cascade, du bas vers le haut : boutons puis liste (le titre, lui, reste et se relabelise)
        Fade-OutCascade @($taskButtonsRow, $taskCenterRow)

        $taskPanel.Visibility = 'Collapsed'
        Reset-Opacity @($taskButtonsRow, $taskCenterRow)

        # Le glyphe (fleche -> coeur) et le titre disparaissent puis reapparaissent ensemble
        Fade-Opacity $backGlyphHost 0 150; Fade-Opacity $panelTitle 0 150
        Wait-Ui 150
        $arrowGlyph.Visibility = 'Collapsed'; $heartIcon.Visibility = 'Visible'
        $panelTitle.Text = 'LittleCleaner'
        Fade-Opacity $backGlyphHost 1 150; Fade-Opacity $panelTitle 1 150

        $menuPanel.Opacity = 0; $menuPanel.Visibility = 'Visible'
        $btnRun.IsEnabled = $true; $btnRun.Content = '[Enter] Exécuter'; $statusText.Text = ''
        Resize-WindowToContent
        Fade-Opacity $menuPanel 1 250
        $btnTool1.IsEnabled = $true; $btnTool2.IsEnabled = $true
        Refresh-Badges   # toujours en dernier : garantit le compte le plus a jour possible
    } else {
        $win.Close()
    }
}
$win.FindName('BtnBack').Add_Click({
    if ($menuPanel.Visibility -eq 'Visible') {
        Animate-HeartBeat   # sur le menu, le "bouton" est le coeur decoratif : juste une petite animation
    } else {
        Go-Back
    }
})

# ---------------------------------------------------------------------------
# Badges de presence sur les cartes du menu (nombre de cibles detectees)
# ---------------------------------------------------------------------------
function Count-Present($steps) {
    $n = 0
    foreach ($s in $steps) {
        if ($s.Group -eq 'Fermer les applications') { continue }   # un processus n'est pas un element a nettoyer
        if (& $s.Check) { $n++ }
    }
    return $n
}

function Update-Badge($badgeBorder, $badgeText, [int]$count) {
    $badgeText.Text = switch ($count) {
        0       { 'Aucun élément' }
        1       { '1 élément' }
        default { "$count éléments" }
    }
    $badgeBorder.Background = if ($count -gt 0) { '#268BD2' } else { '#93A1A1' }
    $badgeBorder.Visibility = 'Visible'
}

function Refresh-Badges {
    Update-Badge $badge1 $badge1Text (Count-Present $Tools[0].Steps)
    Update-Badge $badge2 $badge2Text (Count-Present $Tools[1].Steps)
}

# ---------------------------------------------------------------------------
# Execution
# ---------------------------------------------------------------------------
function Start-Cleanup {
    if (-not $btnRun.IsEnabled) { return }
    $myGen = $script:scanGeneration   # si Back est presse pendant l'execution, ce jeton devient perime
    $btnRun.IsEnabled = $false
    $script:cleanupInProgress = $true
    $ok = 0; $partial = 0; $fail = 0; $skip = 0

    Type-Text $statusText 'C:\> suppression en cours...' 12
    foreach ($s in $script:activeSteps) {
        $r = $script:rows[$s.Name]; $r.Border.ToolTip = $null

        if (-not (& $s.Check)) {
            $r.Status.Text = $SYM_ABS; $r.Status.Foreground = $COL_MUTED
            $skip++
            continue
        }

        Spin-Status $r 10 40
        try {
            $before = & $s.Check
            & $s.Action

            # Marge de reprise : certaines cibles (surtout un processus) mettent un instant
            # a disparaitre completement ; on revalide plusieurs fois avant de conclure a l'echec.
            $after = & $s.Check
            $retry = 0
            while ($after -and $retry -lt 6) {
                Wait-Ui 150
                $after = & $s.Check
                $retry++
            }

            if ($after) {
                if ($after -lt $before) {
                    # Suppression partielle : une partie des elements a ete supprimee, le reste est probablement verrouille/en cours d'utilisation
                    $r.Status.Text = $SYM_PART; $r.Status.Foreground = $COL_PART
                    $r.Border.ToolTip = "Partiel : $after élément(s) sur $before n'ont pas pu être supprimés (probablement en cours d'utilisation)."
                    $partial++
                } else {
                    throw 'La cible est toujours présente après la suppression.'
                }
            } else {
                $r.Status.Text = $SYM_OK; $r.Status.Foreground = $COL_OK; Dim-Line $r
                $ok++
            }
        } catch {
            $r.Status.Text = $SYM_KO; $r.Status.Foreground = $COL_KO
            $r.Border.ToolTip = "Échec : $($_.Exception.Message)"
            $fail++
        }
        Flash-Text $r.Status
        Wait-Ui 60
    }

    Clear-StepsCache $script:activeTool.Title   # l'etat a change, le cache de cet outil n'est plus valide
    Refresh-Badges

    $script:cleanupInProgress = $false

    if ($script:scanGeneration -eq $myGen) {
        # Toujours sur cet outil : on affiche le bilan et on remet le bouton a jour
        if ($script:activeTool.Title -eq $HiddenTool.Title) {
            Type-Text $statusText "C:\> terminé. verdict : le probleme se trouvait entre la chaise et le clavier." 8 $myGen
        } else {
            Type-Text $statusText "C:\> terminé. supprimé $ok, partiel $partial, échec $fail, absent $skip." 8 $myGen
        }
        $btnRun.Content = 'Terminé'
    }
    # Sinon (Back presse pendant l'execution) : on laisse l'etat deja remis a zero par Go-Back tel quel
}
$btnRun.Add_Click({ Start-Cleanup })

# Raccourcis clavier : 1/2/3 choisissent un outil au menu, Entree lance le nettoyage, Echap revient/ferme
$win.Add_PreviewKeyDown({
    param($s, $e)
    switch ($e.Key) {
        { $_ -in 'D1', 'NumPad1' } {
            if ($menuPanel.Visibility -eq 'Visible') { Enter-Tool $Tools[0] }
            $e.Handled = $true
        }
        { $_ -in 'D2', 'NumPad2' } {
            if ($menuPanel.Visibility -eq 'Visible') { Enter-Tool $Tools[1] }
            $e.Handled = $true
        }
        { $_ -in 'D3', 'NumPad3' } {
            if ($menuPanel.Visibility -eq 'Visible') { Enter-Tool $HiddenTool }
            $e.Handled = $true
        }
        'Return' {
            if ($taskPanel.Visibility -eq 'Visible') { Start-Cleanup }
            $e.Handled = $true
        }
        'Escape' {
            Go-Back
            $e.Handled = $true
        }
    }
})

$win.Add_Loaded({
    # Prechauffage silencieux : construit deja les lignes des deux outils en cache
    # (evite de creer ~15 elements WPF "a froid" au tout premier clic), et exerce
    # une fois le systeme d'animation (DoubleAnimation/ColorAnimation/easings) pour
    # que sa compilation JIT ne coute rien lors de la toute premiere vraie transition.
    Build-Steps $Tools[0].Steps
    $snap0 = @(); foreach ($it in $stepList.Items) { $snap0 += $it }
    $script:rowCache[$Tools[0].Title] = @{ Items = $snap0; Rows = $script:rows }

    Build-Steps $Tools[1].Steps
    $snap1 = @(); foreach ($it in $stepList.Items) { $snap1 += $it }
    $script:rowCache[$Tools[1].Title] = @{ Items = $snap1; Rows = $script:rows }

    $stepList.Items.Clear()   # le menu est affiche, pas une page outil : liste vide

    $dummy = New-Object Windows.Media.Animation.DoubleAnimation
    $dummy.From = 1; $dummy.To = 1
    $dummy.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds(1))
    $dummy.EasingFunction = New-Object Windows.Media.Animation.BackEase
    $panelTitle.BeginAnimation([Windows.UIElement]::OpacityProperty, $dummy)
    $heartScale.BeginAnimation([Windows.Media.ScaleTransform]::ScaleXProperty, $dummy)
    $win.Dispatcher.Invoke([action] {}, 'Render')
    $panelTitle.BeginAnimation([Windows.UIElement]::OpacityProperty, $null); $panelTitle.Opacity = 1
    $heartScale.BeginAnimation([Windows.Media.ScaleTransform]::ScaleXProperty, $null); $heartScale.ScaleX = 1

    $dummyBrush = New-Object Windows.Media.SolidColorBrush ([Windows.Media.Colors]::Black)
    $dummyColor = New-Object Windows.Media.Animation.ColorAnimation
    $dummyColor.To = [Windows.Media.Colors]::Black
    $dummyColor.Duration = New-Object Windows.Duration ([timespan]::FromMilliseconds(1))
    $dummyBrush.BeginAnimation([Windows.Media.SolidColorBrush]::ColorProperty, $dummyColor)

    Refresh-Badges
    $win.Height = Get-DesiredWindowHeight   # taille initiale correcte, sans animation
    Type-Text $menuSub 'C:\> sélectionnez un outil' 22
})

$win.ShowDialog() | Out-Null

-> Décryptage des liens du forum : extension "ThiWeb Crypt / Decrypt"™ ou Thiweb Live <-

Avatar du membre
Blik
Nouveau
Nouveau
Messages : 78
Enregistré le : il y a 11 ans

Message par Blik »

@LaDidi21

Pas tant pour cacher le code, mais plutôt pour éviter qu'on demande c'est quoi un fichier .Ps1. As-tu une suggestion quelquonque?
Avatar du membre
LaDidi21
Modérateur
Modérateur
Messages : 14778
Enregistré le : il y a 17 ans

Message par LaDidi21 »

@Blik :
Si ce n'est pas pour cacher, c'est parfait 8|

-> Décryptage des liens du forum : extension "ThiWeb Crypt / Decrypt"™ ou Thiweb Live <-

Répondre

Qui est en ligne

Utilisateurs parcourant ce forum : Amazon [Bot], bart et 11 invités