Getting an up-to-date HTML help from TOTALCMD.CHM at a glance (resulted in a functional online help system by petermad!)

English support forum

Moderators: petermad, Stefan2, white, Hacker

hi5
Power Member
Power Member
Posts: 724
Joined: 2012-11-03, 11:35 UTC
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *hi5 »

petermad wrote: 2026-05-16, 12:53 UTCThat would require inserting that code in all the pages ...
I understand, but that task is also very trivial - The entire process from CHM to "converted" online docs is easily automated with a (very) short batch/script incl. automatic uploading to a website.

(but as I mentioned earlier elsewhere, the process should ideally be done the other way around: start with clean proper html, ready to generate (automatically) various output formats incl. online, chm, hlp, ...)
F4MiniMenu (Forum) - Open selected file(s) from TC in defined editor(s) - A (minimalistic) clone of F4Menu
Source at GitHub (AutoHotkey). TCSyncComments (copy file comments)
User avatar
petermad
Power Member
Power Member
Posts: 17307
Joined: 2003-02-05, 20:24 UTC
Location: Denmark
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *petermad »

Out of curiosity, do you think this is still the best practice?
CHMDir works OK for unpacking. and the 32bit version also works fine for packing - I have been using it intensively these days, and for several years making the totalcmdDark.chm

It is correct that trying to pack with the 64bit version crashes TC - because of that I use this in [PackerPlugins] in wincmd.ini:

Code: Select all

chm=468,%COMMANDER_PATH%\PLUGINS\WCX\CHMDir\CHMDir.wcx
Using 468 prevents TC from trying to pack or delete files.

I have added a "button" in the bottom of toc.html to copy the url for the frameset with the currently displayed help file. that makes it easier to make a link to a page to hand over to others

it means that if the file currently displayed is for example: registration.htm then using this button will copy https://tcmd.madsenworld.dk/onlinehelp/?registration.htm to the Clipboard - ready to paste somewhere else - like in a topic here on the forum.
License #524 1994
Danish TC Translator
TC 11.57 32+64bit on Windows XP 32bit & Windows 7, 8.1 & 10 (22H2) 64bit, 'Everything' 1.5.0.1413b
TC 3.62 on Android 6, 13, 15 & 16
TC Extended Menus | TC Languagebar | TC Dark Help | PHSM-Calendar
User avatar
petermad
Power Member
Power Member
Posts: 17307
Joined: 2003-02-05, 20:24 UTC
Location: Denmark
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *petermad »

2hi5
I understand, but that task is also very trivial
Even so - it is another task to have to do every time a new TC version is out (including betas)

I have now blocked robots from crawling tcmd.madsenworld.dk/onlinehelp/totalcmd.chm and tcmd.madsenworld.dk/onlinehelp-deu/totalcmd.chm
License #524 1994
Danish TC Translator
TC 11.57 32+64bit on Windows XP 32bit & Windows 7, 8.1 & 10 (22H2) 64bit, 'Everything' 1.5.0.1413b
TC 3.62 on Android 6, 13, 15 & 16
TC Extended Menus | TC Languagebar | TC Dark Help | PHSM-Calendar
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 52908
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *ghisler(Author) »

And does Ghisler approve of this?
I don't mind - thanks for all the efford to improve the help!
Author of Total Commander
https://www.ghisler.com
User avatar
beb
Power Member
Power Member
Posts: 729
Joined: 2009-09-20, 08:03 UTC
Location: Odesa, Ukraine

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *beb »

ghisler(Author) wrote: 2026-05-17, 15:26 UTC I don't mind
Thanks.
petermad wrote: 2026-05-14, 14:42 UTC I recommend in the file totalcmd.chm\ie.css to change this line:

Code: Select all

div { font-family:Arial,sans-serif;font-size:x-small }
to:

Code: Select all

div { font-family:Arial,sans-serif;font-size:small; max-width: 50em }
This petermad's recommendation regarding the CSS fix makes actual sense, and there's also an option to automate following it, e.g., via a PowerShell script:

Code: Select all

# optimize original ie.css style as proposed by petermad
''
'trying to optimize original CSS style if required...'
$original = 'div { font-family:Arial,sans-serif;font-size:x-small; }'
$modified = 'div { font-family:Arial,sans-serif;font-size:small;max-width:50em; }'
$style = 'ie.css'
foreach ($item in (Get-ChildItem -path $PSscriptRoot -file -recurse -force -filter $style)){
[IO.Path]::GetRelativePath($PSscriptRoot,$item)
'original style : {0}' -f $original
$text = [IO.File]::ReadAllText($item,[Text.Encoding]::UTF8)
if($text -match $original){
'modified style : {0}' -f $modified
$text = $text.Replace($original,$modified)
[IO.File]::WriteAllText($item,$text,[Text.Encoding]::UTF8)}
else{'no match has been found, nothing to do here...'}
''
} # end of for each item loop
This code can be used as a standalone script (to do so, save the code as code.ps1) or as a part of the decompiling script from the title message (to do so, copy-paste the code into the decompile_totalcmd_help.ps1 after the decompiling command section and before the finalizing section).
#278521 User License
Total Commander [always the latest version, including betas] x86/x64 on Win10 x64/Windows 11/Android 16
User avatar
Native2904
Senior Member
Senior Member
Posts: 422
Joined: 2014-01-30, 14:23 UTC
Location: Aachen

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *Native2904 »

I’ve never studied the help section so thoroughly since it went online... it’s brilliant for looking things up on the go.
Thanks+++
Last edited by Native2904 on 2026-05-19, 05:10 UTC, edited 1 time in total.
Glückauf
#270101 Single user licence.
Total Commander 64-bit – latest version – Windows 11 Pro 24H2
User avatar
beb
Power Member
Power Member
Posts: 729
Joined: 2009-09-20, 08:03 UTC
Location: Odesa, Ukraine

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *beb »

2petermad
Please consider adding an explicit return link to index2.html (Help files list) to get back to index.html in a single step.
Thank you.
#278521 User License
Total Commander [always the latest version, including betas] x86/x64 on Win10 x64/Windows 11/Android 16
User avatar
petermad
Power Member
Power Member
Posts: 17307
Joined: 2003-02-05, 20:24 UTC
Location: Denmark
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *petermad »

beb wrote: 2026-05-18, 11:23 UTC
petermad wrote: 2026-05-14, 14:42 UTC I recommend in the file totalcmd.chm\ie.css to change this line:

Code: Select all

div { font-family:Arial,sans-serif;font-size:x-small }
to:

Code: Select all

div { font-family:Arial,sans-serif;font-size:small; max-width: 50em }
I don't use the max-width anymore. Instead I restricted the width of the help iframe to 50em and made it adjustable with this css:
.help {width:50em; resize: horizontal}
(only works in cromium browsers)
License #524 1994
Danish TC Translator
TC 11.57 32+64bit on Windows XP 32bit & Windows 7, 8.1 & 10 (22H2) 64bit, 'Everything' 1.5.0.1413b
TC 3.62 on Android 6, 13, 15 & 16
TC Extended Menus | TC Languagebar | TC Dark Help | PHSM-Calendar
User avatar
petermad
Power Member
Power Member
Posts: 17307
Joined: 2003-02-05, 20:24 UTC
Location: Denmark
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *petermad »

beb wrote: 2026-05-18, 22:40 UTC 2petermad
Please consider adding an explicit return link to index2.html (Help files list) to get back to index.html in a single step.
Thank you.
Done - Since it is a long list I chose to put the back link in the top.
License #524 1994
Danish TC Translator
TC 11.57 32+64bit on Windows XP 32bit & Windows 7, 8.1 & 10 (22H2) 64bit, 'Everything' 1.5.0.1413b
TC 3.62 on Android 6, 13, 15 & 16
TC Extended Menus | TC Languagebar | TC Dark Help | PHSM-Calendar
User avatar
beb
Power Member
Power Member
Posts: 729
Joined: 2009-09-20, 08:03 UTC
Location: Odesa, Ukraine

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance

Post by *beb »

petermad wrote: 2026-05-19, 09:40 UTC Done - Since it is a long list I chose to put the back link in the top.
Thank you a lot!
#278521 User License
Total Commander [always the latest version, including betas] x86/x64 on Win10 x64/Windows 11/Android 16
User avatar
beb
Power Member
Power Member
Posts: 729
Joined: 2009-09-20, 08:03 UTC
Location: Odesa, Ukraine

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance (resulted in a functional online help system by peterm

Post by *beb »

With Total Commander 11.57a RC1, a minor structural update to the help has happened:
The "Button bar configuration" (dlg_configbuttonbar.htm) entry has appeared in the "Configuration and switches" section of the Contents of the help (TOTALCMD.CHM) (with the corresponding update to the hsc6C4.hhc in the TOTALCMD.CHM).
This entry is already present in toc.html, so no action is required in this regard.
#278521 User License
Total Commander [always the latest version, including betas] x86/x64 on Win10 x64/Windows 11/Android 16
User avatar
petermad
Power Member
Power Member
Posts: 17307
Joined: 2003-02-05, 20:24 UTC
Location: Denmark
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance (resulted in a functional online help system by peterm

Post by *petermad »

I have now added the version of the Help file to the upper right of the help content frame.
License #524 1994
Danish TC Translator
TC 11.57 32+64bit on Windows XP 32bit & Windows 7, 8.1 & 10 (22H2) 64bit, 'Everything' 1.5.0.1413b
TC 3.62 on Android 6, 13, 15 & 16
TC Extended Menus | TC Languagebar | TC Dark Help | PHSM-Calendar
User avatar
Native2904
Senior Member
Senior Member
Posts: 422
Joined: 2014-01-30, 14:23 UTC
Location: Aachen

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance (resulted in a functional online help system by peterm

Post by *Native2904 »

Hello Petermad,

Would it be possible to make the online help (Thank you for implementing this) compatible with mobile devices?

I am not completely sure, but I believe there may be a setting in the HTML header for responsive/mobile display.

<meta name="viewport" content="width=device-width, initial-scale=1.0">
Glückauf
#270101 Single user licence.
Total Commander 64-bit – latest version – Windows 11 Pro 24H2
User avatar
petermad
Power Member
Power Member
Posts: 17307
Joined: 2003-02-05, 20:24 UTC
Location: Denmark
Contact:

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance (resulted in a functional online help system by peterm

Post by *petermad »

<meta name="viewport" content="width=device-width, initial-scale=1.0"> does not have any real positive influence when using frames you can test it here: https://tcmd.madsenworld.dk/onlinehelp/index1a.html

I have made a version where I make the width of the frames smaller when viewed on a smartphone - that helps a little: https://tcmd.madsenworld.dk/onlinehelp/index1b.html
License #524 1994
Danish TC Translator
TC 11.57 32+64bit on Windows XP 32bit & Windows 7, 8.1 & 10 (22H2) 64bit, 'Everything' 1.5.0.1413b
TC 3.62 on Android 6, 13, 15 & 16
TC Extended Menus | TC Languagebar | TC Dark Help | PHSM-Calendar
User avatar
beb
Power Member
Power Member
Posts: 729
Joined: 2009-09-20, 08:03 UTC
Location: Odesa, Ukraine

Re: Getting an up-to-date HTML help from TOTALCMD.CHM at a glance (resulted in a functional online help system by peterm

Post by *beb »

beb wrote: 2026-05-14, 11:41 UTC N.B. It is also possible, and relatively easy, to fully automate the "TOTALCMD.CHM/hsc6C4.hhc" to "toc.html" conversion in case of the future structural updates (which are highly unlikely to be that radical anyway), but that goes out of my curiosity scope at the time.
It turned out to be not that 'relatively easy', but still doable.
The latest structural update to the CHM help, although minor, finally motivated me to make an automation script, and here we are.

Key datails:
  • Fully automated dynamic toc.html creation from the decompiled totalcmd.chm/*.hhc; it takes a fraction of a second to make toc.html with no manual after-party actions required
  • English and German help support
  • The HTML document <body></body> code in its entirety is automatically generated by the PowerShell script
  • The HTML document <header></header> code is plain text within the hereString $html variable of the PowerShell script and therefore can be easily modified/adapted by a user regarding user-specific/preffered HTML styles, javascripts, etc (in the <header></header>, only the document <title></title> is generated by the PowerShell depending on the help language)
  • No more static hard-coded elements like 'sections = ['Index','Intro','General','Operation','Menus','Config','Credits']'
User preferences:
  • $keep_toc_above_hhc = $true
    defines the toc.html file destination path/location:
    $true : make .html one level above .hhc, but not above the PowerShell script (default)
    $false : make .html next to .hhc
  • $language_base_name = $true
    defines the toc.html language-based naming usage:
    $true : language code is added to the name, e.g., toc_ENG.html, toc_DEU.html (default)
    $false : target name is toc.html
  • $overwrite_existing = $true
    defines the toc.html file name collisions handling:
    $true : new file overwrites existing one (default)
    $false : new file will be renamed, e.g. toc_ENG1.html,toc_ENG2.html, etc
  • $specialized_index = $false
    defines specialized help index usage:
    $true : index is wrapped into a separate top-level help Contents entry (as it was up to this day)
    $false : index is a standalone top-level help Contents entry, as in the original help (default)
Obviously, the PowerShell script should be placed anywhere next to or above the .hhc(s).
hhc_to_toc_html_automation.ps1

Code: Select all

$time = [diagnostics.stopwatch]::StartNew()

# ------------------------------
# USER PREFERENCES
# ------------------------------

# toc.html destination path
# $true  : save .html one level above .hhc, but not above this script (default)
# $false : save .html next to .hhc
$keep_toc_above_hhc = $true

# toc.html language based naming
# $true  : language code is added to the name, e.g. toc_ENG.html,toc_DEU.html (default)
# $false : target name is toc.html
$language_base_name = $true

# toc.html filename collisions handling
# $true  : new file overwrites existing one (default)
# $false : new file will be renamed, e.g. toc_ENG1.html,toc_ENG2.html, etc
$overwrite_existing = $true

# specialized help index
# $true  : index is wrapped into a separate top level help Contents entry
# $false : index is a standalone top level entry, as in the original help (default)
$specialized_index = $false

# ------------------------------
# INITIALS
# ------------------------------

# working directory is a directory of this script
$path = $PSScriptRoot

# basic filename
$filename = 'toc.html'

# language-specific entries
$EnglishIndex = 'Help Index'
$GermanIndex  = 'Hilfe-Index'
$EnglishTitle = 'Total Commander Help Contents'
$GermanTitle  = 'Total Commander Hilfe: Inhaltsverzeichnis'
$MenuHeaders  = 'Menus','Menüs'

# ------------------------------
# HTML ENCODING HELPER FUNCTION
# ------------------------------

function Escape-HtmlAttribute {
    param([string]$text)
    if ($null -eq $text) {
        ''
    }
    [Net.WebUtility]::HtmlEncode($text)
}

# ------------------------------
# HTML ID HELPER FUNCTION
# ------------------------------

# initialize id cache
$idCache = @{}

function Get-HtmlId {
    param([string]$text)
    # null input protection
    if ([string]::IsNullOrWhiteSpace($text)) {
        $text = 'section'
    }

    # force lower case
    $id = $text.ToLowerInvariant()

    # transliterate German umlauts
    $id = $id.Replace('ä','ae').Replace('ö','oe').Replace('ü','ue').Replace('ß','ss')

    # replace HTML entity
    $id = $id -replace '&amp;','and'

    # replace non-alphanumeric sequences with dash
    $id = $id -replace '[^a-z0-9]+','-'

    # trim leading/trailing dashes
    $id = $id.Trim('-')

    # fallback if result became empty
    if (-not $id) {
        $id = 'section'
    }

    # ensure id uniqueness
    if ($idCache.ContainsKey($id)) {
        $idCache[$id]++
        $id = '{0}-{1}' -f $id,$idCache[$id]
    }
    else {
        $idCache[$id] = 0
    }

    $id
}

# ------------------------------
# GET .HHC FILES RECURSIVELY
# ------------------------------

$files = Get-ChildItem -path $path -file -recurse -force -filter *.hhc

# ------------------------------
# EXIT IF NO INPUT FILES
# ------------------------------

if ($files.count -eq 0) {
    'nothing to do here...' | Write-Host -f Yellow
    sleep -seconds 3
    exit
}

# ------------------------------
# PROCESS INPUT FILE(S)
# ------------------------------

'processing...' | Write-Host -f Yellow

foreach ($file in $files) {

# ------------------------------
# INPUT ENCODING
# ------------------------------

$UTF8 = [Text.Encoding]::UTF8
$L1 = [Text.Encoding]::Latin1

# language-specific input
$languageSpecific = [IO.File]::ReadAllText($file.FullName,$L1) -match $GermanIndex

# language-specific encoding
$encoding = switch ($true) {
    $languageSpecific {$L1}
    default {$UTF8}
}

# ------------------------------
# READ INPUT FILE
# ------------------------------

$lines = [IO.File]::ReadAllLines($file.FullName,$encoding)

# ------------------------------
# PARSE OBJECTS WITH DEPTH
# ------------------------------

$depth = 0
$current = $null

$items = foreach ($line in $lines) {

if ($line -match '(?i)<ul>') {
    $depth++
    continue
}

if ($line -match '(?i)</ul>') {
    # prevent negative depth
    if ($depth -gt 0) {
        $depth--
    }
    continue
}

if ($line -match '(?i)<object') {
    $current = [ordered]@{
        depth = $depth
        title = ''
        href  = ''
    }
    continue
}

if ($line -match '(?i)name="Name"\s+value="([^"]+)"') {
    if ($current) {
        $current.title = $matches[1]
    }
    continue
}
if ($line -match '(?i)name="Local"\s+value="([^"]+)"') {
    if ($current) {
        $current.href = $matches[1]
    }
    continue
}
if ($line -match '(?i)</object>') {
    if ($current) {
        [PSCustomObject]$current
        $current = $null
    }
}}

# ------------------------------
# HTML GENERATION
# ------------------------------

$body = [Text.StringBuilder]::new()

$insideSection = $false
$insideMenu    = $false
$insideUl      = $false
$idCache.Clear()

# current top-level section id
$currentSectionId = $null

foreach ($item in $items) {
$safeTitle = Escape-HtmlAttribute $item.title
$safeHref  = Escape-HtmlAttribute $item.href

# ------------------------------
# SPECIAL INDEX
# ------------------------------

if ($specialized_index) {
if ($item.depth -eq 1 -and $item.title -in $EnglishIndex,$GermanIndex) {
    $sectionId = Get-HtmlId $item.title
    [void]$body.AppendLine('    <div class="header">')
    [void]$body.AppendLine("        <a class=""header"" onclick=""toggleItem('$sectionId')"">Index</a>")
    [void]$body.AppendLine('    </div>')
    [void]$body.AppendLine("    <div id='$sectionId' class='toggle-section' style=""width: 100%; display: none"">")
    [void]$body.AppendLine('        <ul>')
    [void]$body.AppendLine("            <li><a href=""$safeHref"">$safeTitle</a></li>")
    [void]$body.AppendLine('        </ul>')
    [void]$body.AppendLine('    </div>')
    [void]$body.AppendLine('')
    continue
}}
else {
if ($item.depth -eq 1 -and $item.href) {
    [void]$body.AppendLine('    <div class="header">')
    [void]$body.AppendLine("        <a href=""$safeHref"">$safeTitle</a>")
    [void]$body.AppendLine('    </div>')
}}

# ------------------------------
# TOP LEVEL SECTIONS
# ------------------------------

if ($item.depth -eq 1 -and -not $item.href) {

    # close menu if active
    if ($insideMenu) {
    [void]$body.AppendLine('                </ul>')
    [void]$body.AppendLine('            </div>')
    $insideMenu = $false
    }

    # close ul if active
    if ($insideUl) {
    [void]$body.AppendLine('        </ul>')
    $insideUl = $false
    }

    # close previous section
    if ($insideSection) {
    [void]$body.AppendLine('    </div>')
    [void]$body.AppendLine('')
    }

    # store current section id
    $currentSectionId = Get-HtmlId $item.title
    [void]$body.AppendLine('    <div class="header">')
    [void]$body.AppendLine("        <a class=""header"" onclick=""toggleItem('$currentSectionId')"">$safeTitle</a>")
    [void]$body.AppendLine('    </div>')
    [void]$body.AppendLine("    <div id='$currentSectionId' class='toggle-section' style=""width: 100%; display: none"">")
    $insideSection = $true
    continue
}

# ------------------------------
# DEPTH 2 MENU HEADERS
# ------------------------------

if ($item.depth -eq 2 -and $item.title -in $MenuHeaders) {
    if (-not $insideUl) {
    [void]$body.AppendLine('        <ul>')
    $insideUl = $true
    }
    # menu id bound to current section
    $menuId = Get-HtmlId ('menu-' + $currentSectionId)
    [void]$body.AppendLine('')
    [void]$body.AppendLine('            <div class="menu">')
    [void]$body.AppendLine("                <a class=""menu"" onclick=""toggleItem('$menuId')"">$safeTitle</a>")
    [void]$body.AppendLine('            </div>')
    [void]$body.AppendLine("            <div id='$menuId' class='toggle-section' style=""width: 100%; display: none"">")
    [void]$body.AppendLine('                <ul>')
    $insideMenu = $true
    continue
}

# ------------------------------
# DEPTH 3 MENU ITEMS
# ------------------------------

if ($item.depth -eq 3 -and $insideMenu) {
    [void]$body.AppendLine("                    <li><a href=""$safeHref"">$safeTitle</a></li>")
    continue
}

# ------------------------------
# RETURN FROM MENU TO NORMAL SECTION ITEMS
# ------------------------------

if ($insideMenu -and $item.depth -eq 2) {
    [void]$body.AppendLine('                </ul>')
    [void]$body.AppendLine('            </div>')
    [void]$body.AppendLine('')
    $insideMenu = $false
}

# ------------------------------
# NORMAL SECTION ITEMS
# ------------------------------

if ($item.depth -eq 2 -and $item.href) {
    if (-not $insideUl) {
    [void]$body.AppendLine('        <ul>')
    $insideUl = $true
    }
    [void]$body.AppendLine("            <li><a href=""$safeHref"">$safeTitle</a></li>"
    )
}

} # end of items loop

# ------------------------------
# FINAL CLOSURES
# ------------------------------

if ($insideMenu) {
    [void]$body.AppendLine('                </ul>')
    [void]$body.AppendLine('            </div>')
}

if ($insideUl) {
    [void]$body.AppendLine('        </ul>')
}

if ($insideSection) {
    [void]$body.AppendLine('    </div>')
}

# ------------------------------
# DOCUMENT TITLE
# ------------------------------

# language-specific title
$documentTitle = switch ($true) {
    $languageSpecific {$GermanTitle}
    default {$EnglishTitle}
}
$documentTitle = Escape-HtmlAttribute $documentTitle

# ------------------------------
# HTML TEMPLATE
# ------------------------------

$html = @"
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>$($documentTitle)</title>
<base href="totalcmd.chm/" target="help">

<style>
body {font-family:Arial,sans-serif;font-size:small;margin-left:1em;background-color:#f5f5f5;}
a, a:visited, a:active {text-decoration:none;color:blue;}a:hover{color:#ff9000;}
div {margin-top:0;margin-bottom:0;}
ul {list-style-type:none;margin-left:1em;margin-top:0;margin-bottom:0;padding:0;}
.header {color:black;cursor:pointer;margin-top:1em;user-select:none;}
.button {color:gray;cursor:pointer;font-size:90%;user-select:none;}
.menu {color:black;cursor:pointer;margin-top:0;user-select:none;}
</style>

<script>

function toggleAll(btn) {
    var sections = document.querySelectorAll('.toggle-section');
    var anyClosed = false;
    for (var i = 0; i < sections.length; i++) {
        var el = sections[i];
        if (el.style.display !== 'block') {
            anyClosed = true;
            break;
        }
    }

    var targetState = anyClosed
        ? 'block'
        : 'none';
    for (var i = 0; i < sections.length; i++) {
        sections[i].style.display = targetState;
    }

    btn.innerHTML = anyClosed
        ? '[&#xFF0D;] collapse all'
        : '[&#xFF0B;] expand all';
}

function toggleItem(ItemID) {
    var el = document.getElementById(ItemID);
    if (!el)
        return;
    el.style.display =
        el.style.display === 'none'
            ? 'block'
            : 'none';
}

</script>

</head>
<body>

<div class="button" onclick="toggleAll(this)">
[&#xFF0B;] expand all
</div>

$($body.ToString().Trim())

</body>
</html>
"@

# ------------------------------
# SAVE RESULTING TOC.HTML FILE
# ------------------------------

# use help language in naming
if ($language_base_name) {
$LNG = switch ($true) {
    $languageSpecific {'DEU'}
    default {'ENG'}
    }
$filename = "toc_$LNG.html"
}

# keep toc.html one level above HHC
if ($keep_toc_above_hhc) {
if ($file.Directory.FullName -ne (Get-Item $path).FullName) {
    $directory = $file.Directory.Parent
    }
    else {
    $directory = $file.Directory
    }
}
else {
    $directory = $file.Directory
}

# resulting toc.html full name
$toc = [IO.Path]::combine($directory,$filename)

# if toc.html already exists, rename it sequentially
if (-not $overwrite_existing) {
$base = [IO.Path]::GetFileNameWithoutExtension($toc)
$counter = 1
while ([IO.File]::Exists($toc)) {
    $toc = [IO.Path]::combine($directory,"$base$counter.html")
    $counter++
}}

# write file contents
[IO.File]::WriteAllText($toc,$html,$UTF8)

# display filename relative to the script
$relative = [IO.DirectoryInfo]$path
if ($path -ne (Get-Item $path).PSDrive.Root) {
    $relative = $relative.Parent
}

'{0} file has been written' -f [IO.Path]::GetRelativePath($relative,$toc) | Write-Host -f Cyan

} # end of files loop

# ------------------------------
# FINALIZE
# ------------------------------

$time.Stop()
''
'{0:mm\:ss\.fff}' -f $time.Elapsed | Write-Host -f DarkCyan
'by {0}' -f $MyInvocation.MyCommand.Name

sleep -seconds 7
Now I regret a bit that I did not make such a script earlier.
#278521 User License
Total Commander [always the latest version, including betas] x86/x64 on Win10 x64/Windows 11/Android 16
Post Reply