Add src/ui.ps1

This commit is contained in:
2026-01-14 02:08:43 -05:00
parent ced6c715cc
commit d49a1117dd

196
src/ui.ps1 Normal file
View File

@@ -0,0 +1,196 @@
function Publish-Checkboxes {
param(
[Parameter(Mandatory)][string]$Page,
[string]$Column,
[string]$Group
)
function Escape-HtmlAttr {
param([string]$s)
if ([string]::IsNullOrEmpty($s)) { return '' }
$s = $s -replace "(`r`n|`r|`n)", ' '
$s = $s -replace '&','&'
$s = $s -replace '"','"'
$s = $s -replace "'",'''
$s = $s -replace '<','&lt;'
$s = $s -replace '>','&gt;'
return $s
}
function Escape-HtmlText {
param([string]$s)
if ([string]::IsNullOrEmpty($s)) { return '' }
$s = $s -replace '&','&amp;'
$s = $s -replace '<','&lt;'
$s = $s -replace '>','&gt;'
return $s
}
$tasks = $Global:SamyTasks | Where-Object Page -EQ $Page
if (-not [string]::IsNullOrEmpty($Column)) {
$tasks = $tasks | Where-Object Column -EQ $Column
}
if (-not [string]::IsNullOrEmpty($Group)) {
if ($Group -eq 'tweaks') {
$tasks = $tasks | Where-Object {
-not ($_.PSObject.Properties.Name -contains 'Group') -or
[string]$_.Group -eq '' -or
[string]$_.Group -eq 'tweaks'
}
}
else {
$tasks = $tasks | Where-Object {
($_.PSObject.Properties.Name -contains 'Group') -and
([string]$_.Group -eq $Group)
}
}
}
(
$tasks | ForEach-Object {
$taskId = $_.Id
$rawTooltip = if ($_.PSObject.Properties.Name -contains 'Tooltip' -and $_.Tooltip) { [string]$_.Tooltip } else { [string]$_.Label }
$tooltipText = Escape-HtmlAttr $rawTooltip
$tooltipAttr = if ([string]::IsNullOrWhiteSpace($tooltipText)) { '' } else { " title=`"$tooltipText`"" }
$labelText = Escape-HtmlText ([string]$_.Label)
$taskIdAttr = Escape-HtmlAttr ([string]$taskId)
$nameAttr = Escape-HtmlAttr ([string]$_.Name)
$colAttr = Escape-HtmlAttr ([string]$Column)
$groupValue = ''
if ($_.PSObject.Properties.Name -contains 'Group' -and $_.Group) { $groupValue = [string]$_.Group }
$groupAttr = Escape-HtmlAttr $groupValue
$groupDataAttr = if ([string]::IsNullOrWhiteSpace($groupAttr)) { '' } else { " data-group=""$groupAttr""" }
$html = "<label$tooltipAttr><input type=""checkbox"" id=""$taskIdAttr"" name=""$nameAttr"" data-column=""$colAttr""$groupDataAttr> $labelText</label>"
if ($_.SubOptions) {
$subHtml = (
$_.SubOptions | ForEach-Object {
$type = if ($_.PSObject.Properties.Name -contains 'Type' -and $_.Type) { [string]$_.Type } else { 'checkbox' }
if ($type -eq 'text') {
$subId = Escape-HtmlAttr ([string]$_.Id)
$subLabel = Escape-HtmlText ([string]$_.Label)
$ph = if ($_.PSObject.Properties.Name -contains 'Placeholder') { Escape-HtmlAttr ([string]$_.Placeholder) } else { '' }
$help = if ($_.PSObject.Properties.Name -contains 'Help') { Escape-HtmlText ([string]$_.Help) } else { '' }
@"
<div style="margin-left:20px; margin-top:6px;">
<label for="$subId">$subLabel</label>
<input type="text" id="$subId" placeholder="$ph" />
<small style="display:block; margin-top:4px;">$help</small>
</div>
"@
}
else {
$subLabel = Escape-HtmlText ([string]$_.Label)
$subTaskIdClass = Escape-HtmlAttr ([string]$taskId)
$subValueAttr = Escape-HtmlAttr ([string]$_.Value)
"<label style=""margin-left:20px; display:block;"">
<input type=""checkbox"" class=""sub-option-$subTaskIdClass"" name=""$subValueAttr"" value=""$subValueAttr""> $subLabel
</label>"
}
}
) -join "`n"
$html += @"
<div id="$(Escape-HtmlAttr ([string]$taskId))OptionsContainer" style="display:none; margin-top:4px;">
$subHtml
</div>
"@
}
$html
}
) -join "`n"
}
function Get-ModuleVersionHtml {
$mod = Get-Module -ListAvailable -Name SVSMSP | Sort-Object Version -Descending | Select-Object -First 1
$branchDisplay = switch ($Script:SamyBranch.ToLower()) {
'main' { 'Main / Stable' }
'beta' { 'Beta' }
default { $Script:SamyBranch }
}
if ($mod) {
return "<div style='color:#bbb; font-size:0.9em; margin-top:1em;'>
Module Version: $($mod.Version)<br>
UI Branch: $branchDisplay
</div>"
}
return "<div style='color:#f66;'>SVSMSP_Module not found</div>"
}
function Get-UIHtml {
param([string]$Page = 'onboard')
if (-not $Page) { $Page = 'onboard' }
$onboardLeft = Publish-Checkboxes -Page 'onboard' -Column 'left'
$onboardRightApps = Publish-Checkboxes -Page 'onboard' -Column 'right' -Group 'apps'
$onboardRightTweaks = Publish-Checkboxes -Page 'onboard' -Column 'right' -Group 'tweaks'
$offboard = Publish-Checkboxes -Page 'offboard' -Column ''
$devices = Publish-Checkboxes -Page 'devices' -Column ''
$tasksJson = @(
$Global:SamyTasks | ForEach-Object {
[pscustomobject]@{
id = [string]$_.Id
handler = "/$([string]$_.Name)"
label = [string]$_.Label
}
}
) | ConvertTo-Json -Depth 4
$tasksJsAll = $tasksJson
$cssContent = Get-RemoteText -Url $Script:SamyCssUrl
$jsContent = Get-RemoteText -Url $Script:SamyJsUrl
$htmlTemplate = Get-RemoteText -Url $Script:SamyHtmlUrl
if (-not $htmlTemplate) {
Write-LogHybrid "UI template download failed. Returning minimal error page." Error UI -LogToEvent
return "<html><body style='font-family:Segoe UI; padding:20px; background:#111; color:#ddd;'>
<h2>SAMY UI template unavailable</h2>
<p>Could not download samy.html from repo.</p>
</body></html>"
}
if ($cssContent) {
$cssContent += @"
/* SAMY background override injected by script */
.sidebar::after {
background-image: url('$Script:SamyBgLogoUrl') !important;
}
"@
}
$html = $htmlTemplate
$html = $html.Replace('{{CssContent}}', $cssContent)
$html = $html.Replace('{{JsContent}}', $jsContent)
$html = $html.Replace('{{SamyFaviconUrl}}', $Script:SamyFaviconUrl)
$html = $html.Replace('{{SamyTopLogoUrl}}', $Script:SamyTopLogoUrl)
$html = $html.Replace('{{SamyHintText}}', $Script:SamyHintText)
$html = $html.Replace('{{moduleVersion}}', (Get-ModuleVersionHtml))
$html = $html.Replace('{{onboardLeftColumn}}', $onboardLeft)
$html = $html.Replace('{{onboardRightApps}}', $onboardRightApps)
$html = $html.Replace('{{onboardRightTweaks}}', $onboardRightTweaks)
$html = $html.Replace('{{offboardCheckboxes}}', $offboard)
$html = $html.Replace('{{devicesCheckboxes}}', $devices)
$html = $html.Replace('{{tasksJsAll}}', $tasksJsAll)
$html = $html.Replace('{{defaultPage}}', $Page)
return $html
}