auto(0129): agents_dashboard — secret_store_cpp_infra + CMakeLists register #4
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: powertoys_config_path
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "def powertoys_config_path(user: str | None = None) -> str"
|
||||
description: "Devuelve el path al default.json de PowerToys Keyboard Manager. Soporta WSL, Windows nativo y override via env var POWERTOYS_CONFIG."
|
||||
tags: [powertoys, keyboard, windows, wsl, config, path]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [os, platform]
|
||||
params:
|
||||
- name: user
|
||||
desc: "Nombre de usuario Windows. Si es None, lee $USER del entorno. Solo relevante en WSL."
|
||||
output: "Path absoluto al archivo default.json de PowerToys Keyboard Manager (el archivo puede no existir)."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/infra/powertoys_config_path.py"
|
||||
notes: |
|
||||
Clasificada como pure por convencion del registry: sin estado mutable, determinista dado el entorno.
|
||||
Lee env vars ($POWERTOYS_CONFIG, $USER, $LOCALAPPDATA, $WSL_DISTRO_NAME) que se tratan como
|
||||
parametros implicitos del entorno — comportamiento identico al de funciones puras de configuracion.
|
||||
Prioridad de resolucion: (1) $POWERTOYS_CONFIG override, (2) WSL path con /mnt/c, (3) Windows nativo.
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from infra.powertoys_config_path import powertoys_config_path
|
||||
|
||||
# WSL con usuario actual
|
||||
path = powertoys_config_path()
|
||||
# => /mnt/c/Users/lucas/AppData/Local/Microsoft/PowerToys/Keyboard Manager/default.json
|
||||
|
||||
# Override explicito
|
||||
import os
|
||||
os.environ["POWERTOYS_CONFIG"] = "/tmp/test_powertoys.json"
|
||||
path = powertoys_config_path()
|
||||
# => /tmp/test_powertoys.json
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Detecta WSL verificando `/mnt/c` y las variables `WSL_DISTRO_NAME` / `WSLENV`.
|
||||
En Windows nativo usa `%LOCALAPPDATA%`. El archivo puede no existir si PowerToys no esta instalado.
|
||||
@@ -0,0 +1,51 @@
|
||||
"""Detect the path of PowerToys Keyboard Manager default.json config file."""
|
||||
|
||||
import os
|
||||
import platform
|
||||
|
||||
|
||||
def powertoys_config_path(user: str | None = None) -> str:
|
||||
"""Return the path to PowerToys Keyboard Manager default.json.
|
||||
|
||||
Supports three resolution modes (in priority order):
|
||||
1. POWERTOYS_CONFIG env var override.
|
||||
2. WSL: /mnt/c/Users/<user>/AppData/Local/Microsoft/PowerToys/Keyboard Manager/default.json
|
||||
where <user> is the `user` argument, then $USER env var.
|
||||
3. Native Windows: %LOCALAPPDATA%/Microsoft/PowerToys/Keyboard Manager/default.json
|
||||
|
||||
Args:
|
||||
user: Windows username. If None, reads $USER from env. Only used in WSL mode.
|
||||
|
||||
Returns:
|
||||
Absolute path string to default.json (file may or may not exist).
|
||||
"""
|
||||
override = os.environ.get("POWERTOYS_CONFIG")
|
||||
if override:
|
||||
return override
|
||||
|
||||
relative = "Microsoft/PowerToys/Keyboard Manager/default.json"
|
||||
|
||||
# Detect WSL: check for /mnt/c mount or WSL-specific env vars
|
||||
is_wsl = (
|
||||
os.path.exists("/mnt/c")
|
||||
or "WSL_DISTRO_NAME" in os.environ
|
||||
or "WSLENV" in os.environ
|
||||
)
|
||||
|
||||
if is_wsl:
|
||||
resolved_user = user or os.environ.get("USER") or os.environ.get("USERNAME")
|
||||
if not resolved_user:
|
||||
raise ValueError(
|
||||
"Cannot determine Windows username. Pass user= or set $USER env var."
|
||||
)
|
||||
return f"/mnt/c/Users/{resolved_user}/AppData/Local/{relative}"
|
||||
|
||||
if platform.system() == "Windows":
|
||||
local_app_data = os.environ.get("LOCALAPPDATA", "")
|
||||
if not local_app_data:
|
||||
raise ValueError("$LOCALAPPDATA env var is not set on this Windows system.")
|
||||
return os.path.join(local_app_data, relative)
|
||||
|
||||
# Fallback: assume WSL-style path with $USER
|
||||
resolved_user = user or os.environ.get("USER") or os.environ.get("USERNAME", "")
|
||||
return f"/mnt/c/Users/{resolved_user}/AppData/Local/{relative}"
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: powertoys_restart
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def powertoys_restart() -> None"
|
||||
description: "Mata los procesos de PowerToys y los relanza para que recargue la configuracion del Keyboard Manager. Compatible con WSL via taskkill.exe y cmd.exe."
|
||||
tags: [powertoys, keyboard, windows, wsl, restart, process, taskkill]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: [os, subprocess, time]
|
||||
params: []
|
||||
output: "None. Mata PowerToys.exe y PowerToys.KeyboardManagerEngine.exe, espera 1 segundo, y relanza PowerToys.exe de forma desacoplada."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/infra/powertoys_restart.py"
|
||||
notes: |
|
||||
error_py_core no existe en el registry. Esta funcion puede lanzar RuntimeError si
|
||||
taskkill.exe o cmd.exe no estan disponibles en el PATH, o FileNotFoundError si no
|
||||
se puede resolver el path del exe.
|
||||
|
||||
Mata los procesos con taskkill.exe /F (forzado). Si PowerToys no esta corriendo,
|
||||
taskkill falla silenciosamente (capture_output=True, returncode ignorado).
|
||||
|
||||
El path del exe se construye como /mnt/c/Users/<USER>/AppData/Local/PowerToys/PowerToys.exe.
|
||||
Se puede sobreescribir con la env var $POWERTOYS_EXE (path WSL o Windows).
|
||||
|
||||
La conversion de path WSL a Windows: /mnt/c/Users/... -> C:\Users\... se hace internamente.
|
||||
El Popen usa cmd.exe /c start "" <exe> para desacoplar el proceso hijo (no bloquea).
|
||||
|
||||
Tiempo de espera entre kill y launch: 1 segundo (hardcoded). Si PowerToys tarda mas
|
||||
en cerrar, la nueva instancia puede fallar al iniciar — esperar unos segundos extra
|
||||
antes de verificar si esta corriendo.
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from infra.powertoys_shortcut_add import powertoys_shortcut_add
|
||||
from infra.powertoys_restart import powertoys_restart
|
||||
|
||||
# Anadir atajo y recargar PowerToys
|
||||
powertoys_shortcut_add(
|
||||
keys=["lctrl", "lalt", "t"],
|
||||
target_path=r"C:\Windows\System32\wt.exe",
|
||||
)
|
||||
powertoys_restart()
|
||||
print("PowerToys reiniciado — el nuevo atajo esta activo")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Solo funciona en entornos donde `taskkill.exe` y `cmd.exe` estan disponibles en el PATH
|
||||
(WSL con integracion Windows habilitada, o Windows nativo). No funciona en Linux puro
|
||||
ni en Mac.
|
||||
@@ -0,0 +1,48 @@
|
||||
"""Restart PowerToys so it reloads its Keyboard Manager configuration."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
|
||||
def powertoys_restart() -> None:
|
||||
"""Kill PowerToys processes and relaunch the application.
|
||||
|
||||
Kills PowerToys.exe and PowerToys.KeyboardManagerEngine.exe using taskkill.exe,
|
||||
waits 1 second for them to shut down cleanly, then relaunches PowerToys.exe via
|
||||
cmd.exe /c start. Works from WSL (invokes taskkill.exe and cmd.exe from the
|
||||
Windows system32 path).
|
||||
|
||||
The PowerToys executable is resolved using the $USER env var to build the
|
||||
standard AppData path. To override the exe path, set $POWERTOYS_EXE.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If taskkill.exe or cmd.exe cannot be found/invoked.
|
||||
FileNotFoundError: If the PowerToys.exe path cannot be resolved.
|
||||
"""
|
||||
user = os.environ.get("USER") or os.environ.get("USERNAME", "")
|
||||
powertoys_exe = os.environ.get(
|
||||
"POWERTOYS_EXE",
|
||||
f"/mnt/c/Users/{user}/AppData/Local/PowerToys/PowerToys.exe",
|
||||
)
|
||||
|
||||
# Kill running instances (ignore errors if not running)
|
||||
for process_name in ("PowerToys.exe", "PowerToys.KeyboardManagerEngine.exe"):
|
||||
subprocess.run(
|
||||
["taskkill.exe", "/IM", process_name, "/F"],
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Relaunch via cmd.exe /c start to detach from the current process
|
||||
# Convert WSL path to Windows path for cmd.exe
|
||||
win_exe = powertoys_exe
|
||||
if powertoys_exe.startswith("/mnt/c/"):
|
||||
win_exe = "C:\\" + powertoys_exe[len("/mnt/c/"):].replace("/", "\\")
|
||||
|
||||
subprocess.Popen(
|
||||
["cmd.exe", "/c", "start", "", win_exe],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: powertoys_shortcut_add
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def powertoys_shortcut_add(keys: list[str], target_path: str, args: str = \"\", elevated: bool = False, exact_match: bool = False, start_in_dir: str = \"\", config_path: str | None = None) -> None"
|
||||
description: "Añade o reemplaza un atajo global en el config de PowerToys Keyboard Manager. Convierte nombres legibles de teclas a VK codes y escribe JSON compacto (una linea) para mantener compatibilidad con el formato de PowerToys."
|
||||
tags: [powertoys, keyboard, windows, wsl, shortcut, config, write, add]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: [json, os]
|
||||
params:
|
||||
- name: keys
|
||||
desc: "Lista de nombres de teclas (case-insensitive). Ej: ['lctrl', 'lalt', 't']. Modificadores: lctrl/rctrl/ctrl, lalt/ralt/alt, lshift/rshift/shift, lwin/rwin/win. Letras: a-z. Digitos: 0-9. F-keys: f1-f12. Especiales: space, enter, tab, esc."
|
||||
- name: target_path
|
||||
desc: "Path Windows al ejecutable. Ej: 'C:\\\\Windows\\\\System32\\\\wt.exe'."
|
||||
- name: args
|
||||
desc: "Argumentos de linea de comandos para el programa (default vacio)."
|
||||
- name: elevated
|
||||
desc: "Si True, lanza el programa elevado (runProgramElevationLevel=1). Default False."
|
||||
- name: exact_match
|
||||
desc: "Si True, el atajo requiere coincidencia exacta de teclas. Default False."
|
||||
- name: start_in_dir
|
||||
desc: "Directorio de trabajo para el programa. Default string vacio."
|
||||
- name: config_path
|
||||
desc: "Path al default.json. Si None, usa $POWERTOYS_CONFIG o el path WSL por defecto para $USER."
|
||||
output: "None. Modifica el archivo default.json en disco."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/infra/powertoys_shortcut_add.py"
|
||||
notes: |
|
||||
error_py_core no existe en el registry. Esta funcion lanza excepciones nativas de Python:
|
||||
FileNotFoundError si config_path no existe, json.JSONDecodeError si el JSON es invalido,
|
||||
ValueError si un nombre de tecla no esta en VK_CODES.
|
||||
|
||||
El JSON se escribe en formato compacto (separators=(",", ":")) — sin espacios ni saltos de linea —
|
||||
para mantener compatibilidad con el formato que usa PowerToys en el archivo original.
|
||||
|
||||
El dict VK_CODES mapea "ctrl"->162, "alt"->164, "shift"->160, "win"->91 como aliases
|
||||
a las variantes izquierdas. Para especificar side derecho, usar "rctrl", "ralt", etc.
|
||||
|
||||
Si ya existe una entrada con los mismos originalKeys, la reemplaza en su posicion original.
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from infra.powertoys_shortcut_add import powertoys_shortcut_add
|
||||
|
||||
# Anadir Ctrl+Alt+T -> Windows Terminal
|
||||
powertoys_shortcut_add(
|
||||
keys=["lctrl", "lalt", "t"],
|
||||
target_path=r"C:\Windows\System32\wt.exe",
|
||||
start_in_dir=r"C:\Users\lucas",
|
||||
)
|
||||
|
||||
# Anadir Win+Shift+E -> Explorer elevado
|
||||
powertoys_shortcut_add(
|
||||
keys=["win", "shift", "e"],
|
||||
target_path=r"C:\Windows\explorer.exe",
|
||||
elevated=True,
|
||||
)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Despues de modificar el config, PowerToys necesita reiniciarse para detectar los cambios.
|
||||
Usar `powertoys_restart_py_infra` para recargar la configuracion automaticamente.
|
||||
@@ -0,0 +1,119 @@
|
||||
"""Add or replace a PowerToys Keyboard Manager global shortcut."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
# Windows Virtual Key codes — key name (lowercase) → VK int
|
||||
VK_CODES: dict[str, int] = {
|
||||
"lwin": 91, "rwin": 92,
|
||||
"lshift": 160, "rshift": 161, "shift": 160,
|
||||
"lctrl": 162, "rctrl": 163, "ctrl": 162,
|
||||
"lalt": 164, "ralt": 165, "alt": 164,
|
||||
"space": 32, "enter": 13, "tab": 9, "esc": 27,
|
||||
"win": 91,
|
||||
# F-keys
|
||||
"f1": 112, "f2": 113, "f3": 114, "f4": 115,
|
||||
"f5": 116, "f6": 117, "f7": 118, "f8": 119,
|
||||
"f9": 120, "f10": 121, "f11": 122, "f12": 123,
|
||||
# Digits
|
||||
"0": 48, "1": 49, "2": 50, "3": 51, "4": 52,
|
||||
"5": 53, "6": 54, "7": 55, "8": 56, "9": 57,
|
||||
}
|
||||
# Letters A-Z
|
||||
for _ch in "abcdefghijklmnopqrstuvwxyz":
|
||||
VK_CODES[_ch] = ord(_ch.upper())
|
||||
|
||||
|
||||
def _keys_to_vk_string(keys: list[str]) -> str:
|
||||
"""Convert a list of key names to a semicolon-separated VK code string.
|
||||
|
||||
Key names are case-insensitive. Letters are matched as lowercase.
|
||||
Raises ValueError for unknown key names.
|
||||
"""
|
||||
codes = []
|
||||
for key in keys:
|
||||
normalized = key.lower()
|
||||
if normalized not in VK_CODES:
|
||||
raise ValueError(
|
||||
f"Unknown key name: '{key}'. "
|
||||
f"Use names like 'ctrl', 'alt', 'lctrl', 'lalt', 'shift', 't', 'f1', etc."
|
||||
)
|
||||
codes.append(str(VK_CODES[normalized]))
|
||||
return ";".join(codes)
|
||||
|
||||
|
||||
def _default_config_path() -> str:
|
||||
override = os.environ.get("POWERTOYS_CONFIG")
|
||||
if override:
|
||||
return override
|
||||
user = os.environ.get("USER") or os.environ.get("USERNAME", "")
|
||||
return (
|
||||
f"/mnt/c/Users/{user}/AppData/Local/Microsoft/PowerToys"
|
||||
f"/Keyboard Manager/default.json"
|
||||
)
|
||||
|
||||
|
||||
def powertoys_shortcut_add(
|
||||
keys: list[str],
|
||||
target_path: str,
|
||||
args: str = "",
|
||||
elevated: bool = False,
|
||||
exact_match: bool = False,
|
||||
start_in_dir: str = "",
|
||||
config_path: str | None = None,
|
||||
) -> None:
|
||||
"""Add or replace a global shortcut in PowerToys Keyboard Manager config.
|
||||
|
||||
Converts the human-readable key list to VK code string and writes the entry
|
||||
into remapShortcuts.global. If an entry with the same originalKeys already
|
||||
exists, it is replaced. Writes JSON in compact (single-line) format to
|
||||
maintain compatibility with the format PowerToys uses.
|
||||
|
||||
Args:
|
||||
keys: List of key names (case-insensitive). E.g. ["lctrl", "lalt", "t"].
|
||||
Supported modifiers: lctrl/rctrl/ctrl, lalt/ralt/alt, lshift/rshift/shift,
|
||||
lwin/rwin/win. Letters: a-z. Digits: 0-9. F-keys: f1-f12.
|
||||
Special: space, enter, tab, esc.
|
||||
target_path: Windows path to the executable. E.g. "C:\\Windows\\System32\\wt.exe".
|
||||
args: Command-line arguments for the program (default "").
|
||||
elevated: Whether to run the program elevated (default False).
|
||||
exact_match: Whether the shortcut requires an exact key match (default False).
|
||||
start_in_dir: Working directory for the program. Defaults to "" (empty string).
|
||||
config_path: Path to default.json. If None, uses $POWERTOYS_CONFIG or WSL default.
|
||||
"""
|
||||
if config_path is None:
|
||||
config_path = _default_config_path()
|
||||
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
original_keys_str = _keys_to_vk_string(keys)
|
||||
|
||||
new_entry = {
|
||||
"originalKeys": original_keys_str,
|
||||
"exactMatch": exact_match,
|
||||
"runProgramElevationLevel": 1 if elevated else 0,
|
||||
"operationType": 1,
|
||||
"runProgramAlreadyRunningAction": 1,
|
||||
"runProgramStartWindowType": 0,
|
||||
"runProgramFilePath": target_path,
|
||||
"runProgramArgs": args,
|
||||
"runProgramStartInDir": start_in_dir,
|
||||
"unicodeText": "*Unsupported*",
|
||||
}
|
||||
|
||||
global_list: list[dict] = data.setdefault("remapShortcuts", {}).setdefault("global", [])
|
||||
|
||||
# Replace if same originalKeys already exists
|
||||
replaced = False
|
||||
for i, entry in enumerate(global_list):
|
||||
if entry.get("originalKeys") == original_keys_str:
|
||||
global_list[i] = new_entry
|
||||
replaced = True
|
||||
break
|
||||
|
||||
if not replaced:
|
||||
global_list.append(new_entry)
|
||||
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, separators=(",", ":"), ensure_ascii=False)
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: powertoys_shortcut_list
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def powertoys_shortcut_list(config_path: str | None = None) -> list[dict]"
|
||||
description: "Lee el default.json de PowerToys Keyboard Manager y devuelve los atajos globales con VK codes convertidos a nombres legibles."
|
||||
tags: [powertoys, keyboard, windows, wsl, shortcut, config, read]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: [json, os]
|
||||
params:
|
||||
- name: config_path
|
||||
desc: "Path absoluto al default.json. Si es None, usa $POWERTOYS_CONFIG o el path WSL por defecto para $USER."
|
||||
output: "Lista de dicts con campos: keys (list[str] nombres legibles), target (str path al exe), args (str argumentos), elevated (bool), exact_match (bool), start_in_dir (str)."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/infra/powertoys_shortcut_list.py"
|
||||
notes: |
|
||||
error_py_core no existe en el registry. Esta funcion lanza excepciones nativas de Python:
|
||||
FileNotFoundError si config_path no existe, json.JSONDecodeError si el JSON es invalido.
|
||||
El caller debe manejar estas excepciones. Cuando error_py_core se cree en el registry,
|
||||
actualizar error_type.
|
||||
|
||||
Solo lee remapShortcuts.global (no appSpecific, no remapKeys, no remapKeysToText).
|
||||
El dict VK_CODES esta definido en el modulo — no hay cross-file imports para mantener KISS.
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from infra.powertoys_shortcut_list import powertoys_shortcut_list
|
||||
|
||||
shortcuts = powertoys_shortcut_list()
|
||||
for s in shortcuts:
|
||||
print(s["keys"], "->", s["target"])
|
||||
# ["lctrl", "lalt", "t"] -> C:\Windows\System32\wt.exe
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Mapeo VK codes: lctrl=162, rctrl=163, lalt=164, ralt=165, lshift=160, rshift=161,
|
||||
lwin=91, rwin=92, A-Z=65-90, 0-9=48-57, F1-F12=112-123, space=32, enter=13, tab=9, esc=27.
|
||||
VK codes desconocidos se devuelven como string numerico (ej: "190").
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Read and list PowerToys Keyboard Manager shortcuts as human-readable dicts."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
# Windows Virtual Key codes → readable names
|
||||
VK_CODES: dict[int, str] = {
|
||||
91: "lwin", 92: "rwin",
|
||||
160: "lshift", 161: "rshift",
|
||||
162: "lctrl", 163: "rctrl",
|
||||
164: "lalt", 165: "ralt",
|
||||
32: "space", 13: "enter", 9: "tab", 27: "esc",
|
||||
# F-keys
|
||||
112: "f1", 113: "f2", 114: "f3", 115: "f4",
|
||||
116: "f5", 117: "f6", 118: "f7", 119: "f8",
|
||||
120: "f9", 121: "f10", 122: "f11", 123: "f12",
|
||||
# Digits 0-9
|
||||
48: "0", 49: "1", 50: "2", 51: "3", 52: "4",
|
||||
53: "5", 54: "6", 55: "7", 56: "8", 57: "9",
|
||||
}
|
||||
# Letters A-Z (VK 65-90)
|
||||
for _vk in range(65, 91):
|
||||
VK_CODES[_vk] = chr(_vk).lower()
|
||||
|
||||
|
||||
def _vk_to_name(vk: int) -> str:
|
||||
"""Convert a VK code integer to a readable key name."""
|
||||
return VK_CODES.get(vk, str(vk))
|
||||
|
||||
|
||||
def _parse_keys(original_keys: str) -> list[str]:
|
||||
"""Parse a semicolon-separated VK code string to a list of readable names."""
|
||||
if not original_keys:
|
||||
return []
|
||||
return [_vk_to_name(int(code)) for code in original_keys.split(";") if code]
|
||||
|
||||
|
||||
def powertoys_shortcut_list(config_path: str | None = None) -> list[dict]:
|
||||
"""Return all global shortcuts from PowerToys Keyboard Manager config.
|
||||
|
||||
Reads the default.json file and returns the list of remapShortcuts.global entries
|
||||
with VK codes converted to human-readable key names.
|
||||
|
||||
Args:
|
||||
config_path: Absolute path to default.json. If None, uses the POWERTOYS_CONFIG
|
||||
env var or the default WSL path for the current $USER.
|
||||
|
||||
Returns:
|
||||
List of dicts with keys: keys, target, args, elevated, exact_match, start_in_dir.
|
||||
"""
|
||||
if config_path is None:
|
||||
config_path = os.environ.get("POWERTOYS_CONFIG")
|
||||
if config_path is None:
|
||||
user = os.environ.get("USER") or os.environ.get("USERNAME", "")
|
||||
config_path = (
|
||||
f"/mnt/c/Users/{user}/AppData/Local/Microsoft/PowerToys"
|
||||
f"/Keyboard Manager/default.json"
|
||||
)
|
||||
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
shortcuts = data.get("remapShortcuts", {}).get("global", [])
|
||||
result = []
|
||||
for entry in shortcuts:
|
||||
result.append({
|
||||
"keys": _parse_keys(entry.get("originalKeys", "")),
|
||||
"target": entry.get("runProgramFilePath", ""),
|
||||
"args": entry.get("runProgramArgs", ""),
|
||||
"elevated": entry.get("runProgramElevationLevel", 0) != 0,
|
||||
"exact_match": entry.get("exactMatch", False),
|
||||
"start_in_dir": entry.get("runProgramStartInDir", ""),
|
||||
})
|
||||
return result
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: powertoys_shortcut_remove
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def powertoys_shortcut_remove(keys: list[str], config_path: str | None = None) -> bool"
|
||||
description: "Elimina un atajo global del config de PowerToys Keyboard Manager por combinacion de teclas. Devuelve True si se elimino, False si no existia."
|
||||
tags: [powertoys, keyboard, windows, wsl, shortcut, config, write, remove, delete]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: [json, os]
|
||||
params:
|
||||
- name: keys
|
||||
desc: "Lista de nombres de teclas (case-insensitive) que identifican el atajo a eliminar. Debe coincidir exactamente con las teclas usadas al crear el atajo. Ej: ['lctrl', 'lalt', 't']."
|
||||
- name: config_path
|
||||
desc: "Path al default.json. Si None, usa $POWERTOYS_CONFIG o el path WSL por defecto para $USER."
|
||||
output: "True si se encontro y elimino el atajo, False si no existia ninguna entrada con esas teclas."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "python/functions/infra/powertoys_shortcut_remove.py"
|
||||
notes: |
|
||||
error_py_core no existe en el registry. Esta funcion lanza excepciones nativas de Python:
|
||||
FileNotFoundError si config_path no existe, json.JSONDecodeError si el JSON es invalido,
|
||||
ValueError si un nombre de tecla no esta en VK_CODES.
|
||||
|
||||
La coincidencia se hace contra el string originalKeys canonico (semicolon-separated VK codes).
|
||||
Si las teclas se pasaron en un orden diferente al guardado, no coincidiran — el orden importa.
|
||||
|
||||
El JSON se escribe en formato compacto (separators=(",", ":")) sin espacios ni saltos de linea.
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from infra.powertoys_shortcut_remove import powertoys_shortcut_remove
|
||||
|
||||
removed = powertoys_shortcut_remove(["lctrl", "lalt", "t"])
|
||||
if removed:
|
||||
print("Atajo eliminado correctamente")
|
||||
else:
|
||||
print("El atajo no existia")
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Despues de modificar el config, PowerToys necesita reiniciarse para detectar los cambios.
|
||||
Usar `powertoys_restart_py_infra` para recargar la configuracion automaticamente.
|
||||
@@ -0,0 +1,89 @@
|
||||
"""Remove a PowerToys Keyboard Manager global shortcut by key combination."""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
# Windows Virtual Key codes — key name (lowercase) → VK int
|
||||
VK_CODES: dict[str, int] = {
|
||||
"lwin": 91, "rwin": 92,
|
||||
"lshift": 160, "rshift": 161, "shift": 160,
|
||||
"lctrl": 162, "rctrl": 163, "ctrl": 162,
|
||||
"lalt": 164, "ralt": 165, "alt": 164,
|
||||
"space": 32, "enter": 13, "tab": 9, "esc": 27,
|
||||
"win": 91,
|
||||
# F-keys
|
||||
"f1": 112, "f2": 113, "f3": 114, "f4": 115,
|
||||
"f5": 116, "f6": 117, "f7": 118, "f8": 119,
|
||||
"f9": 120, "f10": 121, "f11": 122, "f12": 123,
|
||||
# Digits
|
||||
"0": 48, "1": 49, "2": 50, "3": 51, "4": 52,
|
||||
"5": 53, "6": 54, "7": 55, "8": 56, "9": 57,
|
||||
}
|
||||
# Letters A-Z
|
||||
for _ch in "abcdefghijklmnopqrstuvwxyz":
|
||||
VK_CODES[_ch] = ord(_ch.upper())
|
||||
|
||||
|
||||
def _keys_to_vk_string(keys: list[str]) -> str:
|
||||
"""Convert a list of key names to a semicolon-separated VK code string."""
|
||||
codes = []
|
||||
for key in keys:
|
||||
normalized = key.lower()
|
||||
if normalized not in VK_CODES:
|
||||
raise ValueError(
|
||||
f"Unknown key name: '{key}'. "
|
||||
f"Use names like 'ctrl', 'alt', 'lctrl', 'lalt', 'shift', 't', 'f1', etc."
|
||||
)
|
||||
codes.append(str(VK_CODES[normalized]))
|
||||
return ";".join(codes)
|
||||
|
||||
|
||||
def _default_config_path() -> str:
|
||||
override = os.environ.get("POWERTOYS_CONFIG")
|
||||
if override:
|
||||
return override
|
||||
user = os.environ.get("USER") or os.environ.get("USERNAME", "")
|
||||
return (
|
||||
f"/mnt/c/Users/{user}/AppData/Local/Microsoft/PowerToys"
|
||||
f"/Keyboard Manager/default.json"
|
||||
)
|
||||
|
||||
|
||||
def powertoys_shortcut_remove(
|
||||
keys: list[str],
|
||||
config_path: str | None = None,
|
||||
) -> bool:
|
||||
"""Remove a global shortcut from PowerToys Keyboard Manager config by key combination.
|
||||
|
||||
Converts the key list to a VK code string and removes the matching entry from
|
||||
remapShortcuts.global. Writes back in compact JSON format.
|
||||
|
||||
Args:
|
||||
keys: List of key names (case-insensitive) identifying the shortcut to remove.
|
||||
Must match exactly the keys used when the shortcut was created.
|
||||
config_path: Path to default.json. If None, uses $POWERTOYS_CONFIG or WSL default.
|
||||
|
||||
Returns:
|
||||
True if a shortcut was found and removed, False if no matching shortcut existed.
|
||||
"""
|
||||
if config_path is None:
|
||||
config_path = _default_config_path()
|
||||
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
original_keys_str = _keys_to_vk_string(keys)
|
||||
|
||||
global_list: list[dict] = data.get("remapShortcuts", {}).get("global", [])
|
||||
original_len = len(global_list)
|
||||
filtered = [e for e in global_list if e.get("originalKeys") != original_keys_str]
|
||||
|
||||
if len(filtered) == original_len:
|
||||
return False
|
||||
|
||||
data["remapShortcuts"]["global"] = filtered
|
||||
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, separators=(",", ":"), ensure_ascii=False)
|
||||
|
||||
return True
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: error
|
||||
lang: py
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
algebraic: sum
|
||||
definition: |
|
||||
class RegistryError(Exception):
|
||||
"""Error base del registry Python."""
|
||||
description: "Tipo de error base del registry Python. Referenciado como error_type por funciones impuras en Python. Subclase de Exception que marca errores esperados en funciones con efectos secundarios."
|
||||
tags: [error, exception, core, impure, base]
|
||||
uses_types: []
|
||||
file_path: "python/types/core/error_py_core.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from core.error_py_core import RegistryError
|
||||
|
||||
def my_impure_function(path: str) -> str:
|
||||
try:
|
||||
with open(path) as f:
|
||||
return f.read()
|
||||
except OSError as e:
|
||||
raise RegistryError(f"Cannot read {path}: {e}") from e
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Analogo a `error_go_core` (tipo Go). Las funciones impuras Python del registry
|
||||
declaran `error_type: error_py_core` en su frontmatter para indicar que pueden
|
||||
lanzar excepciones en vez de retornar valores invalidos.
|
||||
|
||||
En la mayoria de casos las funciones lanzan excepciones nativas de Python
|
||||
(FileNotFoundError, json.JSONDecodeError, ValueError) — RegistryError actua como
|
||||
clase base opcional para errores de dominio especificos.
|
||||
@@ -0,0 +1,9 @@
|
||||
"""Tipo de error base para funciones impuras Python del registry."""
|
||||
|
||||
|
||||
class RegistryError(Exception):
|
||||
"""Error base del registry Python.
|
||||
|
||||
Subclase de Exception que marca errores esperados en funciones impuras.
|
||||
Las funciones impuras del registry lo lanzan en vez de retornar None o -1.
|
||||
"""
|
||||
Reference in New Issue
Block a user