From a38d6b86c5a3ae0b940f4e487e2328faa8443fad Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 21 Sep 2026 14:49:11 -0700 Subject: [PATCH 1/2] LT-22788: Put the parse-words-in-text choices in a submenu Turn the Parser menu's "Parse Words in Text" item into a submenu holding "All Words in Text" and "Words Without an Approved Analysis". The second one runs what the separate "Parse Unapproved Words in Text" item used to run, so that item is gone. Rename the listener handlers to match the new commands, and name the wordform helper after what it collects. "Unapproved" is left free for the third choice LT-22789 adds. The submenu gets its own display handler so the whole thing hides when there is no current text, as both items did before. Co-Authored-By: Claude Opus 5 --- .../Configuration/Words/areaConfiguration.xml | 10 ++++--- Src/LexText/ParserUI/ParserListener.cs | 29 +++++++++++++------ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml index cc48419c5b..2e74bf6e61 100644 --- a/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml +++ b/DistFiles/Language Explorer/Configuration/Words/areaConfiguration.xml @@ -14,8 +14,8 @@ - - + + @@ -281,8 +281,10 @@ - - + + + + diff --git a/Src/LexText/ParserUI/ParserListener.cs b/Src/LexText/ParserUI/ParserListener.cs index 0fdb519046..a24085f25c 100644 --- a/Src/LexText/ParserUI/ParserListener.cs +++ b/Src/LexText/ParserUI/ParserListener.cs @@ -517,7 +517,7 @@ public bool OnParseCurrentWord(object argument) return true; //we handled this. } - public bool OnDisplayParseWordsInCurrentText(object commandObject, ref UIItemDisplayProperties display) + public bool OnDisplayParseWordsInTextMenu(object commandObject, ref UIItemDisplayProperties display) { CheckDisposed(); @@ -528,7 +528,7 @@ public bool OnDisplayParseWordsInCurrentText(object commandObject, ref UIItemDis return true; //we handled this. } - public bool OnDisplayParseUnapprovedWordsInCurrentText(object commandObject, ref UIItemDisplayProperties display) + public bool OnDisplayParseAllWordsInCurrentText(object commandObject, ref UIItemDisplayProperties display) { CheckDisposed(); @@ -539,7 +539,18 @@ public bool OnDisplayParseUnapprovedWordsInCurrentText(object commandObject, ref return true; //we handled this. } - public bool OnParseWordsInCurrentText(object argument) + public bool OnDisplayParseWordsWithoutApprovedAnalysis(object commandObject, ref UIItemDisplayProperties display) + { + CheckDisposed(); + + bool enable = CurrentText != null; + display.Visible = enable; + display.Enabled = enable; + + return true; //we handled this. + } + + public bool OnParseAllWordsInCurrentText(object argument) { CheckDisposed(); @@ -553,23 +564,23 @@ public bool OnParseWordsInCurrentText(object argument) return true; //we handled this. } - public bool OnParseUnapprovedWordsInCurrentText(object argument) + public bool OnParseWordsWithoutApprovedAnalysis(object argument) { CheckDisposed(); if (CurrentText != null && ConnectToParser()) { IStText text = CurrentText; - IEnumerable wordforms = GetUnapprovedWordforms(text); + IEnumerable wordforms = GetWordformsWithoutApprovedAnalysis(text); UpdateWordforms(wordforms, ParserPriority.Medium); } return true; //we handled this. } - private IEnumerable GetUnapprovedWordforms(IStText text) + private IEnumerable GetWordformsWithoutApprovedAnalysis(IStText text) { - HashSet unapprovedWordforms = new HashSet(); + HashSet wordformsWithoutApprovedAnalysis = new HashSet(); foreach (IStTxtPara para in text.ParagraphsOS) { foreach (ISegment seg in para.SegmentsOS) @@ -590,13 +601,13 @@ private IEnumerable GetUnapprovedWordforms(IStText text) } if (!approved) { - unapprovedWordforms.Add(wordform); + wordformsWithoutApprovedAnalysis.Add(wordform); } } } } } - return unapprovedWordforms; + return wordformsWithoutApprovedAnalysis; } private bool HasApprovedAnalysis(IWfiWordform wordform) From 25e7168abe678d8e8ccaddb6178f1a7c85480880 Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Mon, 21 Sep 2026 15:28:17 -0700 Subject: [PATCH 2/2] Make the fieldworks-winapp scripts run under Windows PowerShell 5.1 Replace the em dashes in Preflight-WinFormsMcp.ps1 and Resolve-FieldWorksDevRegistry.ps1 with ASCII. Neither file has a UTF-8 BOM, so PowerShell 5.1 decodes each em dash as three cp1252 characters, the last of which is a curly quote that ends the enclosing string. The registry script died on a line 14 lines past the one it named. Also expand the two null-conditional `?.Source` expressions in the preflight, which PowerShell 5.1 cannot parse at all. Both scripts now run to completion under 5.1, which is the only PowerShell on a stock FieldWorks dev box. Co-Authored-By: Claude Opus 5 --- .../scripts/Preflight-WinFormsMcp.ps1 | 25 ++++++++++--------- .../scripts/Resolve-FieldWorksDevRegistry.ps1 | 6 ++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.claude/skills/fieldworks-winapp/scripts/Preflight-WinFormsMcp.ps1 b/.claude/skills/fieldworks-winapp/scripts/Preflight-WinFormsMcp.ps1 index 951f4ac835..23e8927d8f 100644 --- a/.claude/skills/fieldworks-winapp/scripts/Preflight-WinFormsMcp.ps1 +++ b/.claude/skills/fieldworks-winapp/scripts/Preflight-WinFormsMcp.ps1 @@ -5,14 +5,14 @@ .DESCRIPTION The winforms-mcp server (@fnrhombus/winforms-mcp) is launched by the MCP client itself (Claude Code reads .mcp.json; VS Code reads .vscode/mcp.json). This script does NOT start - the server — it verifies the prerequisites that make `winforms_launch_app` succeed, and + the server -- it verifies the prerequisites that make `winforms_launch_app` succeed, and prints the exact FieldWorks.exe path to launch. Run it before a parity/screenshot session. Checks: 1. node + npx are on PATH (the server runs under npx). 2. The @fnrhombus/winforms-mcp package resolves on the registry (will be fetched on first use). - 3. Output//FieldWorks.exe exists (the app the MCP drives) — else points at build.ps1. - 4. ICU_DATA is discoverable (FieldWorks needs it; mirrors test.ps1's resolution) — advisory. + 3. Output//FieldWorks.exe exists (the app the MCP drives), else build.ps1. + 4. ICU_DATA is discoverable (FieldWorks needs it; mirrors test.ps1's resolution), advisory. 5. .mcp.json registers winforms-mcp for Claude Code (the fix for "no winforms_* tools"). .PARAMETER Configuration @@ -49,24 +49,25 @@ Write-Host "winforms-mcp preflight (FieldWorks WinForms control)" -ForegroundCol Write-Host "Repo: $repoRoot`n" # 1. node + npx -$node = (Get-Command node -ErrorAction SilentlyContinue)?.Source -$npx = (Get-Command npx -ErrorAction SilentlyContinue)?.Source +$nodeCmd = Get-Command node -ErrorAction SilentlyContinue +$node = if ($nodeCmd) { $nodeCmd.Source } else { $null } +$npxCmd = Get-Command npx -ErrorAction SilentlyContinue +$npx = if ($npxCmd) { $npxCmd.Source } else { $null } Write-Check ([bool]$node) "node on PATH" $node Write-Check ([bool]$npx) "npx on PATH" $npx -# 2. package resolves (validate a real semver; a stray error string must not count as success). -# Routed through `cmd /c` because PowerShell mangles the leading '@' of a scoped package name — the -# same Windows quirk that makes the MCP server itself launch via `cmd /c npx` in .mcp.json. +# 2. package resolves (a real semver; a stray error string must not count as success). +# `cmd /c` because PowerShell mangles a scoped name's '@', as .mcp.json does for the server. $pkgVersion = $null try { $pkgVersion = (& cmd /c 'npm view @fnrhombus/winforms-mcp version 2>NUL' | Select-Object -First 1) } catch {} $pkgOk = ($pkgVersion -match '^\d+\.\d+\.\d+') Write-Check $pkgOk "@fnrhombus/winforms-mcp resolves" ` - ($(if ($pkgOk) { "version $pkgVersion (fetched on first MCP use)" } else { 'npm view failed — check network/registry' })) + ($(if ($pkgOk) { "version $pkgVersion (fetched on first MCP use)" } else { 'npm view failed -- check network/registry' })) # 3. FieldWorks.exe $exe = Join-Path $repoRoot "Output/$Configuration/FieldWorks.exe" Write-Check (Test-Path $exe) "FieldWorks.exe built ($Configuration)" ` - ($(if (Test-Path $exe) { "winforms_launch_app path: $exe" } else { "missing — run: .\build.ps1 -Configuration $Configuration" })) + ($(if (Test-Path $exe) { "winforms_launch_app path: $exe" } else { "missing -- run: .\build.ps1 -Configuration $Configuration" })) # 4. ICU_DATA (advisory; FieldWorks needs ICU at runtime) $icuOk = $false @@ -76,13 +77,13 @@ else { Select-Object -First 1 if ($cand) { $icuOk = $true; Write-Host " (set ICU_DATA=$($cand.FullName) if FieldWorks fails to start)" -ForegroundColor DarkGray } } -Write-Check $icuOk "ICU data discoverable" $(if ($icuOk) { 'advisory' } else { 'advisory — FieldWorks may need ICU_DATA set' }) +Write-Check $icuOk "ICU data discoverable" $(if ($icuOk) { 'advisory' } else { 'advisory -- FieldWorks may need ICU_DATA set' }) # 5. .mcp.json registers winforms-mcp for Claude Code $mcpJson = Join-Path $repoRoot '.mcp.json' $registered = (Test-Path $mcpJson) -and ((Get-Content $mcpJson -Raw) -match 'winforms-mcp') Write-Check $registered ".mcp.json registers winforms-mcp (Claude Code)" ` - ($(if ($registered) { 'reconnect Claude Code to load the winforms_* tools (approve the project server when prompted)' } else { 'missing — Claude Code will not expose winforms_* tools' })) + ($(if ($registered) { 'reconnect Claude Code to load the winforms_* tools (approve the project server when prompted)' } else { 'missing -- Claude Code will not expose winforms_* tools' })) if ($PrewarmPackage -and $npx) { Write-Host "`nPre-warming the MCP package..." -ForegroundColor Cyan diff --git a/.claude/skills/fieldworks-winapp/scripts/Resolve-FieldWorksDevRegistry.ps1 b/.claude/skills/fieldworks-winapp/scripts/Resolve-FieldWorksDevRegistry.ps1 index 8c3c1a4d3e..760887e098 100644 --- a/.claude/skills/fieldworks-winapp/scripts/Resolve-FieldWorksDevRegistry.ps1 +++ b/.claude/skills/fieldworks-winapp/scripts/Resolve-FieldWorksDevRegistry.ps1 @@ -6,14 +6,14 @@ .DESCRIPTION A FieldWorks dev build resolves its code/config (DistFiles: parts, layouts, configuration) from HKCU\SOFTWARE\SIL\FieldWorks\9 RootCodeDir/RootDataDir. When those point at a DIFFERENT worktree than - the exe being launched, the main window can fail to build (blank window, empty UIA tree) — so this + the exe launched, the main window can fail to build (blank window, empty UIA tree), so this skill aligns them to the running worktree before launch. Because that registry is shared across all worktrees, this script will NOT clobber it if the other worktree is actively relying on it. "Actively using it" = (a) a FieldWorks.exe process is currently running from the other worktree's tree, OR (b) the FieldWorks registry key was last written within the last 24 hours (FieldWorks writes these - dirs on startup, so a recent write ≈ a recent launch from some worktree). + dirs on startup, so a recent write ~= a recent launch from some worktree). If neither holds, the script realigns the registry to this worktree automatically. If either holds, it prints `RESULT=ASK_USER` and changes nothing, so the caller can ask the user before realigning. @@ -110,5 +110,5 @@ if ($recent) { } Set-DevDirs $thisDist -Write-Host "[OK] Other worktree not active and not used in 24h — realigned the dev registry to this worktree." -ForegroundColor Green +Write-Host "[OK] Other worktree not active and not used in 24h -- realigned the dev registry to this worktree." -ForegroundColor Green Write-Host "RESULT=REALIGNED"