diff --git a/Microsoft.PowerShell_profile.ps1 b/Microsoft.PowerShell_profile.ps1 new file mode 100644 index 0000000..e47e25d --- /dev/null +++ b/Microsoft.PowerShell_profile.ps1 @@ -0,0 +1,215 @@ +Invoke-Expression (& { (zoxide init powershell --cmd cd | Out-String) }) + + +Invoke-Expression (&starship init powershell) + +Enable-TransientPrompt + +function reboot {shutdown /r /t 0} + +function lz {eza -l --color=always --group-directories-first --icons} + + +# DO NOT MODIFY -- coreutils -- 60b36fc6-2d59-49df-be51-28dd2f4c3c9a +# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +# Inlining the template into the profile shaves off ~10ms (25%). +$script:__COREUTILS__ = [System.Collections.Generic.HashSet[string]]::new( + [string[]]@( + 'arch', 'b2sum', 'base32', 'base64', 'basename', + 'basenc', 'cat', 'cksum', 'comm', 'cp', + 'csplit', 'cut', 'date', 'df', 'dirname', + 'du', 'echo', 'env', 'expr', 'factor', + 'false', 'find', 'fmt', 'fold', 'grep', + 'head', 'hostname', 'join', 'la', 'link', + 'ln', 'ls', 'md5sum', 'mkdir', 'mktemp', + 'mv', 'nl', 'nproc', 'numfmt', 'od', + 'pathchk', 'pr', 'printenv', 'printf', 'ptx', + 'pwd', 'readlink', 'realpath', 'rm', 'rmdir', + 'seq', 'sha1sum', 'sha224sum', 'sha256sum', 'sha384sum', + 'sha512sum', 'shuf', 'sleep', 'sort', 'split', + 'stat', 'sum', 'tac', 'tail', 'tee', + 'test', 'touch', 'tr', 'true', 'truncate', + 'tsort', 'unexpand', 'uniq', 'unlink', 'uptime', + 'wc', 'xargs', 'yes' + ), + [System.StringComparer]::OrdinalIgnoreCase +) + +$script:__COREUTILS_FAST_SKIP__ = [regex]::new( + '\b(?:' + ($script:__COREUTILS__ -join '|') + ')\b', + [System.Text.RegularExpressions.RegexOptions]::Compiled -bor ` + [System.Text.RegularExpressions.RegexOptions]::IgnoreCase +) + +# Casting the scriptblock to Func once and reusing it avoids the +# per-FindAll scriptblock-to-delegate wrapping overhead (~1.7x faster). +$script:__COREUTILS_CMD_PREDICATE__ = [System.Func[System.Management.Automation.Language.Ast, bool]] { + param($n) $n -is [System.Management.Automation.Language.CommandAst] +} + +$script:__COREUTILS_ARG_SPECIAL__ = [char[]] @("'", '"', '`', '$') + +# Wrap arguments into quotes. By being a function we can properly handle $variables. +# As per MSVCRT, any `\` before `"` must be doubled to escape them. +function global:__coreutils_q { + param($s) + '"' + (([string]$s) -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"' +} + +# PowerShell tokenizes `*"a"*` as [BareWord] instead of the expected [DoubleQuoted, BareWord, DoubleQuoted]. +# To work around that we use... regex. Group 1 = 'single', 2 = "double", 3 = `escape, 4 = bare run. +$script:__COREUTILS_ARG_RX__ = [regex]::new( + "'((?:[^']|'')*)'|""((?:[^""``]|""""|``.)*)""|``(.)|([^'""``]+)", + [System.Text.RegularExpressions.RegexOptions]::Compiled +) +$script:__COREUTILS_ARG_EVAL__ = [System.Text.RegularExpressions.MatchEvaluator] { + param($m) + if ($m.Groups[1].Success) { + # Single-quoted: literal. PS '' -> ', then MSVCRT-quote. + $body = $m.Groups[1].Value.Replace("''", "'") + if ($body -match '^(.*?)(\\+)$') { + return '"' + ($matches[1] -replace '(\\*)"', '$1$1\"') + '"' + $matches[2] + } + return '"' + ($body -replace '(\\*)"', '$1$1\"') + '"' + } + if ($m.Groups[2].Success) { + # Double-quoted: collapse PS quote-escapes to raw " / ', let ExpandString + # resolve `n / `t / $var, then MSVCRT-quote. + $body = $m.Groups[2].Value. + Replace('`"', '"'). + Replace("``'", "'"). + Replace('""', '"') + $body = $ExecutionContext.InvokeCommand.ExpandString($body) + if ($body -match '^(.*?)(\\+)$') { + return '"' + ($matches[1] -replace '(\\*)"', '$1$1\"') + '"' + $matches[2] + } + return '"' + ($body -replace '(\\*)"', '$1$1\"') + '"' + } + if ($m.Groups[3].Success) { + # Backtick-escaped char outside a string: " -> \"; everything else + # becomes a one-char quoted region so glob metas stay literal. + $c = $m.Groups[3].Value + if ($c -eq '"') { + return '\"' + } + return '"' + $c + '"' + } + # Bare run: passed through unquoted so coreutils can glob it; expand $vars. + return $ExecutionContext.InvokeCommand.ExpandString($m.Groups[4].Value) +} + +# PSConsoleHostReadLine override that rewrites coreutils command names to their +# .cmd equivalents after PSReadLine returns (history keeps the original). +# +# Why .cmd over .exe: PSNativeCommandArgumentPassing = 'Windows' results in a behavior +# where passing bare quotes to CreateProcess() is impossible. This prevents us from +# passing "*" as "*" to coreutils and instead will be given as a bare *. +# This causes it to treat it as a glob pattern. "*.cmd" files however are automatically +# treated as PSNativeCommandArgumentPassing = 'Legacy', which preserves quotes. +# It is the only possible workaround and the only way coreutils can work at all. +function PSConsoleHostReadLine { + [System.Diagnostics.DebuggerHidden()] + param() + + $lastRunStatus = $? + Microsoft.PowerShell.Core\Set-StrictMode -Off + $line = [Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($host.Runspace, $ExecutionContext, $lastRunStatus) + + # If the line contains no coreutils name, we don't need to parse the AST at all. + if (-not $script:__COREUTILS_FAST_SKIP__.IsMatch($line)) { + return $line + } + + $ast = [System.Management.Automation.Language.Parser]::ParseInput($line, [ref]$null, [ref]$null) + $commands = $ast.FindAll($script:__COREUTILS_CMD_PREDICATE__, $true) + + # Process right-to-left so earlier offsets stay valid after each splice. + # In-place reverse beats Sort-Object for the typical 1-command line. + if ($commands.Count -gt 1) { + $commands = [System.Collections.Generic.List[object]]::new($commands) + $commands.Reverse() + } + + foreach ($cmd in $commands) { + $name = $cmd.GetCommandName() + if (!$name) { + continue + } + + $baseName = $name + if ($name.EndsWith('.exe') -or $name.EndsWith('.cmd')) { + $baseName = $name.Substring(0, $name.Length - 4) + } + if (!$script:__COREUTILS__.Contains($baseName)) { + continue + } + + # ls/la get colour + listing flags injected; la also rewrites to ls. + $cmdElement = $cmd.CommandElements[0] + $start = $cmdElement.Extent.StartOffset + $end = $cmdElement.Extent.EndOffset + $replacement = "& 'C:\Program Files\coreutils\cmd\" + + switch ($baseName) { + 'la' { $replacement += "ls.cmd' --color=auto -AFhl" } + 'ls' { $replacement += "ls.cmd' --color=auto" } + default { $replacement += "$baseName.cmd'" } + } + + # Walk command elements, merging adjacent ones whose extents touch + # (e.g. `'a'*` parses as [SingleQuoted, BareWord] but is one shell word). + # The inverse case `*'a'*` parses as a single BareWord whose text + # contains the embedded quotes, which is why AST-only analysis + # isn't enough and we still need to re-tokenize the source span. + $argsStart = $end + $argsEnd = $cmd.Extent.EndOffset + $rewrittenArgs = '' + $elements = $cmd.CommandElements + $count = $elements.Count + $i = 1 + while ($i -lt $count) { + $first = $elements[$i] + $wordStart = $first.Extent.StartOffset + $wordEnd = $first.Extent.EndOffset + $merged = $false + while ($i + 1 -lt $count -and $elements[$i + 1].Extent.StartOffset -eq $wordEnd) { + $i++ + $wordEnd = $elements[$i].Extent.EndOffset + $merged = $true + } + $source = $line.Substring($wordStart, $wordEnd - $wordStart) + $rewrittenArgs += $line.Substring($argsStart, $wordStart - $argsStart) + $argsStart = $wordEnd + # IndexOfAny beats running the regex per arg. + if ($source.IndexOfAny($script:__COREUTILS_ARG_SPECIAL__) -lt 0) { + $rewrittenArgs += $source + $i++ + continue + } + # A single un-merged PS expression that needs $var resolution + # (bare $var, "...$var...", $x.Member, $($expr), etc.). + # Defer evaluation to runtime so the value reaches coreutils as a literal arg. + # This matches POSIX behaviour where variable expansions don't result in globbing. + if (-not $merged -and + ($first -is [System.Management.Automation.Language.VariableExpressionAst] -or + $first -is [System.Management.Automation.Language.ExpandableStringExpressionAst] -or + $first -is [System.Management.Automation.Language.MemberExpressionAst])) { + $rewrittenArgs += '(__coreutils_q ' + $source + ')' + $i++ + continue + } + # Slow path: re-tokenise and re-emit as MSVCRT-style quoting, + # then wrap in PS single quotes so PS hands the body verbatim. + $windowsQuoted = $script:__COREUTILS_ARG_RX__.Replace($source, $script:__COREUTILS_ARG_EVAL__) + $rewrittenArgs += "'" + $windowsQuoted.Replace("'", "''") + "'" + $i++ + } + $rewrittenArgs += $line.Substring($argsStart, $argsEnd - $argsStart) + + $line = $line.Substring(0, $start) + $replacement + $rewrittenArgs + $line.Substring($argsEnd) + } + + return $line +} +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# DO NOT MODIFY -- coreutils -- 60b36fc6-2d59-49df-be51-28dd2f4c3c9a diff --git a/yasb/yasb.log b/yasb/yasb.log index 196b733..d127f03 100644 --- a/yasb/yasb.log +++ b/yasb/yasb.log @@ -614,3 +614,58 @@ 2026-06-03 06:24:57,636 [DEBUG] [MainThread] [cli_server/cli_server.py:209]: CLI server stopped 2026-06-03 06:24:58,188 [INFO] [MainThread] [root/bar_manager.py:79]: Successfully loaded updated config and re-initialised all bars. 2026-06-03 06:24:58,256 [INFO] [MainThread] [root/bar_manager.py:104]: Stopping SystemEventListener... +2026-06-03 06:24:59,262 [INFO] [MainThread] [root/log.py:95]: YASB v1.9.0 +2026-06-03 06:24:59,263 [INFO] [CLIPipeServer] [cli_server/cli_server.py:215]: CLI server started v1.1.6 +2026-06-03 06:24:59,263 [INFO] [MainThread] [cli_server/cli_server.py:106]: Log pipe server started +2026-06-03 06:24:59,394 [DEBUG] [MainThread] [qasync._windows._EventPoller/_windows.py:215]: Starting (proactor: <_IocpProactor overlapped#=0 result#=0>)... +2026-06-03 06:24:59,394 [DEBUG] [Dummy-3] [qasync._windows._EventWorker/_windows.py:195]: Thread started +2026-06-03 06:24:59,932 [INFO] [MainThread] [root/service.py:52]: AudioOutputService starting... +2026-06-03 06:25:01,182 [INFO] [MainThread] [root/bar_manager.py:88]: Starting SystemEventListener... +2026-06-03 06:25:01,184 [INFO] [MainThread] [root/watcher.py:65]: Watching directory: c:\users\administrator\.config\yasb +2026-06-03 06:25:01,184 [INFO] [MainThread] [root/watcher.py:104]: Created file watcher +2026-06-03 06:25:01,480 [INFO] [MainThread] [root/update_service.py:429]: Background update checker started +2026-06-03 06:25:52,388 [DEBUG] [Thread-8] [root/watcher.py:84]: Stylesheet modified: c:\users\administrator\.config\yasb\styles.css +2026-06-03 06:26:35,422 [INFO] [MainThread] [root/bar_helper.py:526]: AppBarManager need to re-register 1 bar +2026-06-03 06:26:35,437 [INFO] [MainThread] [root/bar_helper.py:535]: Re-registered AppBar for yasb-bar_QXL0001_a89c5ef2 (space reservation + fullscreen) +2026-06-03 06:28:00,931 [INFO] [MainThread] [root/bar_helper.py:526]: AppBarManager need to re-register 1 bar +2026-06-03 06:28:00,939 [INFO] [MainThread] [root/bar_helper.py:535]: Re-registered AppBar for yasb-bar_QXL0001_a89c5ef2 (space reservation + fullscreen) +2026-06-03 06:37:06,800 [ERROR] [MainThread] [root/base.py:124]: Failed to execute callback of type 'toggle_menu' with args: [] +Traceback (most recent call last): + File "D:\a\yasb\yasb\src\core\widgets\base.py", line 122, in _run_callback +KeyError: 'toggle_menu' +2026-06-03 06:37:07,624 [ERROR] [MainThread] [root/base.py:124]: Failed to execute callback of type 'toggle_menu' with args: [] +Traceback (most recent call last): + File "D:\a\yasb\yasb\src\core\widgets\base.py", line 122, in _run_callback +KeyError: 'toggle_menu' +2026-06-03 06:38:50,207 [INFO] [MainThread] [root/bar_helper.py:526]: AppBarManager need to re-register 1 bar +2026-06-03 06:38:50,219 [INFO] [MainThread] [root/bar_helper.py:535]: Re-registered AppBar for yasb-bar_QXL0001_a89c5ef2 (space reservation + fullscreen) +2026-06-03 06:39:07,385 [INFO] [MainThread] [root/bar_helper.py:526]: AppBarManager need to re-register 1 bar +2026-06-03 06:39:07,395 [INFO] [MainThread] [root/bar_helper.py:535]: Re-registered AppBar for yasb-bar_QXL0001_a89c5ef2 (space reservation + fullscreen) +2026-06-03 06:40:08,038 [INFO] [MainThread] [root/bar_helper.py:526]: AppBarManager need to re-register 1 bar +2026-06-03 06:40:08,051 [INFO] [MainThread] [root/bar_helper.py:535]: Re-registered AppBar for yasb-bar_QXL0001_a89c5ef2 (space reservation + fullscreen) +2026-06-03 08:07:26,680 [INFO] [MainThread] [root/log.py:95]: YASB v1.9.0 +2026-06-03 08:07:26,680 [INFO] [CLIPipeServer] [cli_server/cli_server.py:215]: CLI server started v1.1.6 +2026-06-03 08:07:26,681 [INFO] [MainThread] [cli_server/cli_server.py:106]: Log pipe server started +2026-06-03 08:07:26,681 [ERROR] [MainThread] [root/main.py:75]: Another instance of the YASB is already running. +2026-06-03 08:12:46,756 [INFO] [MainThread] [root/bar_manager.py:104]: Stopping SystemEventListener... +2026-06-03 08:13:36,125 [INFO] [MainThread] [root/log.py:95]: YASB v1.9.0 +2026-06-03 08:13:36,126 [INFO] [CLIPipeServer] [cli_server/cli_server.py:215]: CLI server started v1.1.6 +2026-06-03 08:13:36,127 [INFO] [MainThread] [cli_server/cli_server.py:106]: Log pipe server started +2026-06-03 08:13:36,342 [DEBUG] [MainThread] [qasync._windows._EventPoller/_windows.py:215]: Starting (proactor: <_IocpProactor overlapped#=0 result#=0>)... +2026-06-03 08:13:36,344 [DEBUG] [Dummy-3] [qasync._windows._EventWorker/_windows.py:195]: Thread started +2026-06-03 08:13:37,126 [INFO] [MainThread] [root/service.py:52]: AudioOutputService starting... +2026-06-03 08:13:39,198 [INFO] [MainThread] [root/bar_manager.py:88]: Starting SystemEventListener... +2026-06-03 08:13:39,199 [INFO] [MainThread] [root/watcher.py:65]: Watching directory: c:\users\administrator\.config\yasb +2026-06-03 08:13:39,199 [INFO] [MainThread] [root/watcher.py:104]: Created file watcher +2026-06-03 08:13:39,570 [INFO] [MainThread] [root/update_service.py:429]: Background update checker started +2026-06-03 08:15:46,681 [INFO] [MainThread] [root/bar_manager.py:104]: Stopping SystemEventListener... +2026-06-03 08:16:44,742 [INFO] [MainThread] [root/log.py:95]: YASB v1.9.0 +2026-06-03 08:16:44,742 [INFO] [CLIPipeServer] [cli_server/cli_server.py:215]: CLI server started v1.1.6 +2026-06-03 08:16:44,743 [INFO] [MainThread] [cli_server/cli_server.py:106]: Log pipe server started +2026-06-03 08:16:44,970 [DEBUG] [MainThread] [qasync._windows._EventPoller/_windows.py:215]: Starting (proactor: <_IocpProactor overlapped#=0 result#=0>)... +2026-06-03 08:16:44,971 [DEBUG] [Dummy-3] [qasync._windows._EventWorker/_windows.py:195]: Thread started +2026-06-03 08:16:45,766 [INFO] [MainThread] [root/service.py:52]: AudioOutputService starting... +2026-06-03 08:16:47,674 [INFO] [MainThread] [root/bar_manager.py:88]: Starting SystemEventListener... +2026-06-03 08:16:47,676 [INFO] [MainThread] [root/watcher.py:65]: Watching directory: c:\users\administrator\.config\yasb +2026-06-03 08:16:47,676 [INFO] [MainThread] [root/watcher.py:104]: Created file watcher +2026-06-03 08:16:48,043 [INFO] [MainThread] [root/update_service.py:429]: Background update checker started