Skip to content

feat(ctld-tools): TUI polish — i18n_lang setting, descriptions, double-click (CTLD-TOOLS-TUI-POLISH)#55

Merged
davidp57 merged 4 commits into
developfrom
feature/ctld-tools-tui-polish
Jul 21, 2026
Merged

feat(ctld-tools): TUI polish — i18n_lang setting, descriptions, double-click (CTLD-TOOLS-TUI-POLISH)#55
davidp57 merged 4 commits into
developfrom
feature/ctld-tools-tui-polish

Conversation

@davidp57

@davidp57 davidp57 commented Jul 21, 2026

Copy link
Copy Markdown
Member

Lot CTLD-TOOLS-TUI-POLISH

Improvements surfaced while testing the interactive TUI (follow-up to CTLD-TOOLS-TUI, PR #52). One lot, three parts.

1. Runtime fix — interface language is a real setting

  • i18n_lang (en/fr/es/ko) is now a first-class setting: added to the engine defaults; ctld.tr resolves the active language via ctld.gs("i18n_lang") (config wins), falling back to the module global then "en".
  • It was a bare global read only by tr, so setting it from the user-config silently did nothing. It now works, and appears in the TUI with a value list. busted spec covers selection / default / fallback / user-config-driven.

2. Setting descriptions (shown + searchable)

  • CTLD_config_schema.yaml gains a bilingual description per setting — 73 seeded once from the mission-maker config docs (markdown stripped). The schema is now their source of truth (roadmap: generate the doc tables from it).
  • The "Set setting" picker shows each setting's description in the current language and the filter searches name and description.
  • FilterablePicker now takes (value, label, search) items (label ≠ returned value ≠ searchable text). Descriptions embedded in reference.json.

3. Double-click launch + Unblock doc

  • A command-less invocation in an interactive terminal (an Explorer double-click is one) opens the TUI (_bridge_target, pure + tested); --help and non-tty stdout are left to Typer.
  • Docs (ctld-tools.md EN+FR) note the Windows Unblock step for a downloaded .exe and that double-clicking opens the editor.

Tests / build

  • 109 Python tests (ruff/format/mypy clean). Parity fixture (+1 line, i18n_lang) and embedded bundle regenerated; CTLD.lua rebuilt. Lua lint/busted run in CI.

Closes CTLD-TOOLS-TUI-POLISH (PRD in .backlog/).

Summary by Sourcery

Polish the ctld-tools TUI by making the CTLD interface language a configurable setting, enriching settings with bilingual descriptions that are searchable in the editor, and improving usability with double‑click launch behavior and updated mission‑maker documentation.

New Features:

  • Expose the CTLD interface language (i18n_lang) as a first-class setting with a defined default and enumerated values, available in the ctld-tools TUI.
  • Allow the ctld-tools settings picker to display bilingual descriptions per setting and to search across setting names and descriptions.
  • Support launching the ctld-tools interactive TUI directly when the executable is run without a command in an interactive terminal, including Explorer double-click.

Enhancements:

  • Centralize setting descriptions in the CTLD settings schema as the source of truth, with a reference helper for description lookup by language.
  • Extend the FilterablePicker widget to support distinct value, label, and search text so lists can show rich labels while remaining searchable.
  • Update the internal roadmap and backlog to reflect the new schema-driven documentation approach and the TUI polish lot.

Documentation:

  • Update mission-maker ctld-tools documentation (EN/FR) to describe double-click launch behavior and the Windows Unblock step for downloaded executables.
  • Document the i18n_lang setting and its default in the configuration reference tables.
  • Describe the plan to generate configuration tables from the CTLD settings schema in the developer roadmap.

Tests:

  • Add Lua tests to verify that i18n_lang is driven by the configuration setting, including defaults, fallbacks, and user-config behavior.
  • Add Python tests ensuring i18n_lang appears as a scalar setting with enum choices and that setting descriptions are bilingual and fall back to English.
  • Add TUI tests confirming the settings picker can search by description text and a CLI bridge test covering double-click routing to the TUI.

…e-click (CTLD-TOOLS-TUI-POLISH)

Improvements surfaced while testing the interactive TUI (follow-up to CTLD-TOOLS-TUI).

Runtime fix:
- i18n_lang (the CTLD interface language) is now a first-class setting: added to the
  engine defaults, and ctld.tr resolves the active language via ctld.gs("i18n_lang")
  (config wins), falling back to the module global then "en". It was a bare global read
  only by tr, so setting it from the user-config silently did nothing; it now works and
  appears in the TUI with a value list (schema choices en/fr/es/ko).

Tooling:
- Setting descriptions: CTLD_config_schema.yaml carries a bilingual `description` per
  setting (73 seeded once from the mission-maker config docs); the "Set setting" picker
  shows each description in the current language and is searchable by it. Embedded in the
  reference bundle; the schema is now their source of truth.
- FilterablePicker takes (value, label, search) items so the displayed label differs from
  the returned value and the searchable text.
- Double-click launch: a command-less invocation in an interactive terminal (an Explorer
  double-click is one — a fresh console) opens the TUI (VMCT approach). Docs note the
  Windows Unblock step for a downloaded .exe.

Parity fixture and embedded bundle regenerated; CTLD.lua rebuilt. busted spec covers the
i18n_lang selection. Docs (EN+FR), CHANGELOG, PRD/roadmap/backlog index updated.
@sourcery-ai

sourcery-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Makes CTLD interface language a real, configurable setting, adds bilingual setting descriptions that are shown and searchable in the TUI, and introduces a double-click bridge so ctld-tools.exe opens the TUI directly, with supporting tests and documentation updates.

Sequence diagram for double-click CLI bridge to TUI

sequenceDiagram
    actor User
    participant ctld_tools_exe as ctld_tools.exe
    participant cli_main as cli.main
    participant bridge as _bridge_target
    participant TyperApp as app
    participant TUI as tui

    User->>ctld_tools_exe: double-click
    ctld_tools_exe->>cli_main: main()
    cli_main->>cli_main: parse argv for --lang
    cli_main->>bridge: _bridge_target(argv, sys.stdout.isatty())

    alt [non-tty or --help or explicit command]
        bridge-->>cli_main: return None
        cli_main->>TyperApp: app()
        TyperApp->>User: CLI help/command output
    else [interactive, no command]
        bridge-->>cli_main: return ["tui", *argv]
        cli_main->>cli_main: sys.argv = [prog] + target
        cli_main->>TyperApp: app()
        TyperApp->>TUI: run tui command
        TUI->>User: interactive editor UI
    end
Loading

File-Level Changes

Change Details Files
Make i18n_lang a first-class, user-configurable setting used by runtime translation and exposed in the tools reference.
  • Add i18n_lang default under advanced/mm_facing in CTLD_config.yaml and CTLD_config_defaults via rebuild.
  • Refactor CTLD_i18n.lua/CTLD.lua translation to resolve active language via ctld.gs("i18n_lang") with guarded fallback to module global and then "en".
  • Extend i18n unit specs to cover default language, configured language, fallback on unknown language, and user-config-driven language.
  • Expose i18n_lang as a scalar setting with enum choices in ctld-tools Reference, reference.json, and tests, and document it in configuration docs and changelog.
src/CTLD_config.yaml
CTLD.lua
src/CTLD_i18n.lua
tests/ci/unit/i18n_spec.lua
tools/ctld-tools/ctld_tools/reference.py
tools/ctld-tools/tests/test_reference.py
docs/mission-maker/configuration.md
docs/mission-maker/configuration.fr.md
CHANGELOG.md
Add bilingual descriptions for CTLD settings and make the TUI settings picker display and search by these descriptions.
  • Populate CTLD_config_schema.yaml with bilingual description entries for ~73 settings, including i18n_lang choices.
  • Extend ctld-tools Reference with setting_description(name, lang) that falls back to English and returns None for undocumented settings.
  • Update TUI settings form to build FilterablePicker items as (value, label, search), where label/search include setting descriptions in the current language.
  • Refactor FilterablePicker to support (value, label, search) items, use a shared matches() helper for case-insensitive substring search, and return the option value via Option.id.
  • Add TUI test ensuring the settings picker can find i18n_lang by a word in its description, not just its name.
  • Regenerate embedded reference bundle and test fixtures so descriptions are available at runtime.
src/CTLD_config_schema.yaml
tools/ctld-tools/ctld_tools/reference.py
tools/ctld-tools/ctld_tools/tui/forms.py
tools/ctld-tools/ctld_tools/tui/widgets.py
tools/ctld-tools/ctld_tools/tui/filter.py
tools/ctld-tools/tests/test_app.py
tools/ctld-tools/tests/test_reference.py
tools/ctld-tools/ctld_tools/data/reference.json
tools/ctld-tools/tests/data/reference_settings.json
dev/roadmap.md
Introduce a CLI bridge that routes command-less invocations in a TTY (e.g. Explorer double-click) to the TUI, and document the Windows Unblock and double-click behavior.
  • Add _bridge_target(args, is_tty) helper that rewrites argv to call the tui command when running in an interactive terminal with no explicit subcommand, preserving global options like --lang but ignoring help calls.
  • Invoke the bridge from ctld-tools main() before Typer runs, based on sys.stdout.isatty(), so double-clicking ctld-tools.exe opens the TUI by default.
  • Add focused unit tests for the bridge to cover TTY vs non-TTY behavior, help handling, explicit commands, and carrying global options.
  • Update ctld-tools mission-maker docs (EN/FR) to explain that double-clicking ctld-tools.exe opens the interactive editor and that Windows may require unblocking the downloaded .exe, and reference the setting descriptions/search behavior.
  • Record the lot and bridge behavior in backlog PRD and tickets, including acceptance criteria and roadmap notes.
tools/ctld-tools/ctld_tools/cli.py
tools/ctld-tools/tests/test_cli_bridge.py
docs/mission-maker/ctld-tools.md
docs/mission-maker/ctld-tools.fr.md
.backlog/CTLD-TOOLS-TUI-POLISH/PRD.md
.backlog/CTLD-TOOLS-TUI-POLISH/tickets/01-i18n-lang-as-setting.md
.backlog/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The _bridge_target helper assumes --lang is the only value-taking global option; if you later introduce other global flags with values, this logic will need updating or generalizing to avoid misclassifying commands.
  • In FilterablePicker._item, non-string options are assumed to be (value, label, search) triples; consider validating tuple length or type to fail fast if callers pass an unexpected structure.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `_bridge_target` helper assumes `--lang` is the only value-taking global option; if you later introduce other global flags with values, this logic will need updating or generalizing to avoid misclassifying commands.
- In `FilterablePicker._item`, non-string options are assumed to be `(value, label, search)` triples; consider validating tuple length or type to fail fast if callers pass an unexpected structure.

## Individual Comments

### Comment 1
<location path="src/CTLD_i18n.lua" line_range="67-73" />
<code_context>
+--- Active language: the config setting wins (so the MM can set it from the user-config),
+--- then the module global `ctld.i18n_lang` (pre-config default), then "en". Guarded so a
+--- very early tr() call — before CTLDConfig exists — cannot throw.
+local function _activeLang()
+    local ok, fromSetting = pcall(function() return ctld.gs and ctld.gs("i18n_lang") end)
+    return (ok and fromSetting) or ctld.i18n_lang or "en"
+end
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The broad `pcall` around `ctld.gs("i18n_lang")` can hide real configuration errors and makes debugging harder.

Because the `pcall` wraps all of `ctld.gs`, any exception (including real bugs or misconfigurations) is silently converted into a fallback to `ctld.i18n_lang`/"en". That can hide problems in `ctld.gs` or the settings backend and just surface as "language keeps defaulting to EN" with no signal of the underlying error. Consider restricting what you catch (e.g., a specific "config not ready" error) or at least logging failures so genuine issues aren’t masked.

```suggestion
--- Active language: the config setting wins (so the MM can set it from the user-config),
--- then the module global `ctld.i18n_lang` (pre-config default), then "en".
--- Guarded so a very early tr() call — before CTLDConfig exists — cannot throw, but
--- configuration/backend errors are logged instead of being silently swallowed.
local function _activeLang()
    -- If the settings getter is not available yet, fall back to the pre-config default/English.
    if not ctld.gs then
        return ctld.i18n_lang or "en"
    end

    -- Read the language from config. Errors are logged so misconfigurations are visible.
    local ok, fromSetting = pcall(ctld.gs, "i18n_lang")
    if not ok then
        ctld.utils.log(
            "ERROR",
            "CTLDi18n._activeLang: failed to read 'i18n_lang' from settings: %s",
            tostring(fromSetting)
        )
    end

    return (ok and fromSetting) or ctld.i18n_lang or "en"
end
```
</issue_to_address>

### Comment 2
<location path="docs/mission-maker/ctld-tools.fr.md" line_range="29" />
<code_context>

 ## L'éditeur interactif (`ctld-tools tui`) — recommandé { #the-interactive-editor-ctld-tools-tui-recommended }

+**Double-cliquer `ctld-tools.exe`** ouvre directement cet éditeur (aucun argument requis). Depuis un
+terminal :
+
</code_context>
<issue_to_address>
**suggestion (typo):** Infinitive form here is slightly awkward; consider an imperative or noun phrase.

The wording "**Double-cliquer `ctld-tools.exe`**" sounds unnatural in French because of the infinitive used as the subject. Consider "**Double-cliquez sur `ctld-tools.exe`**" or "**Le double-clic sur `ctld-tools.exe`** ouvre directement cet éditeur" for more idiomatic phrasing.

Suggested implementation:

```
**Double-cliquez sur `ctld-tools.exe`** pour ouvrir directement cet éditeur (aucun argument requis). Depuis un
terminal :

```

Le fichier semble déjà contenir des instructions similaires (« Double-cliquez dessus ») avant et après le bloc concerné. Il peut être utile, pour harmoniser la documentation, de vérifier que toutes les occurrences utilisent la même forme impérative (« Double-cliquez sur … » ou « Double-cliquez dessus ») et d’éviter les répétitions exactes si ce bloc est dupliqué ailleurs dans le fichier.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/CTLD_i18n.lua
Comment on lines +67 to +73
--- Active language: the config setting wins (so the MM can set it from the user-config),
--- then the module global `ctld.i18n_lang` (pre-config default), then "en". Guarded so a
--- very early tr() call — before CTLDConfig exists — cannot throw.
local function _activeLang()
local ok, fromSetting = pcall(function() return ctld.gs and ctld.gs("i18n_lang") end)
return (ok and fromSetting) or ctld.i18n_lang or "en"
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): The broad pcall around ctld.gs("i18n_lang") can hide real configuration errors and makes debugging harder.

Because the pcall wraps all of ctld.gs, any exception (including real bugs or misconfigurations) is silently converted into a fallback to ctld.i18n_lang/"en". That can hide problems in ctld.gs or the settings backend and just surface as "language keeps defaulting to EN" with no signal of the underlying error. Consider restricting what you catch (e.g., a specific "config not ready" error) or at least logging failures so genuine issues aren’t masked.

Suggested change
--- Active language: the config setting wins (so the MM can set it from the user-config),
--- then the module global `ctld.i18n_lang` (pre-config default), then "en". Guarded so a
--- very early tr() call — before CTLDConfig exists — cannot throw.
local function _activeLang()
local ok, fromSetting = pcall(function() return ctld.gs and ctld.gs("i18n_lang") end)
return (ok and fromSetting) or ctld.i18n_lang or "en"
end
--- Active language: the config setting wins (so the MM can set it from the user-config),
--- then the module global `ctld.i18n_lang` (pre-config default), then "en".
--- Guarded so a very early tr() call — before CTLDConfig exists — cannot throw, but
--- configuration/backend errors are logged instead of being silently swallowed.
local function _activeLang()
-- If the settings getter is not available yet, fall back to the pre-config default/English.
if not ctld.gs then
return ctld.i18n_lang or "en"
end
-- Read the language from config. Errors are logged so misconfigurations are visible.
local ok, fromSetting = pcall(ctld.gs, "i18n_lang")
if not ok then
ctld.utils.log(
"ERROR",
"CTLDi18n._activeLang: failed to read 'i18n_lang' from settings: %s",
tostring(fromSetting)
)
end
return (ok and fromSetting) or ctld.i18n_lang or "en"
end


## L'éditeur interactif (`ctld-tools tui`) — recommandé { #the-interactive-editor-ctld-tools-tui-recommended }

**Double-cliquer `ctld-tools.exe`** ouvre directement cet éditeur (aucun argument requis). Depuis un

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (typo): Infinitive form here is slightly awkward; consider an imperative or noun phrase.

The wording "Double-cliquer ctld-tools.exe" sounds unnatural in French because of the infinitive used as the subject. Consider "Double-cliquez sur ctld-tools.exe" or "Le double-clic sur ctld-tools.exe ouvre directement cet éditeur" for more idiomatic phrasing.

Suggested implementation:

**Double-cliquez sur `ctld-tools.exe`** pour ouvrir directement cet éditeur (aucun argument requis). Depuis un
terminal :

Le fichier semble déjà contenir des instructions similaires (« Double-cliquez dessus ») avant et après le bloc concerné. Il peut être utile, pour harmoniser la documentation, de vérifier que toutes les occurrences utilisent la même forme impérative (« Double-cliquez sur … » ou « Double-cliquez dessus ») et d’éviter les répétitions exactes si ce bloc est dupliqué ailleurs dans le fichier.

davidp57 added 2 commits July 21, 2026 17:05
…-only)

Putting i18n_lang in the engine defaults made ctld.gs("i18n_lang") always return "en",
shadowing the legacy `ctld.i18n_lang = "fr"` method and breaking the F-103 i18n specs.
Declare it in CTLD_config_schema.yaml with `default: en` instead; Reference surfaces
schema settings with a default into scalar_settings. gs stays nil unless the user-config
sets it, so the legacy global still works and the user-config wins when present. busted
i18n_lang spec adjusted; parity fixture reverts to no i18n_lang.
The new i18n_lang describe nulled CTLDConfig._instance without reloading, so later specs
inherited an empty instance (36 failures). after_each now reloads the defaults.
@davidp57
davidp57 merged commit 9830a41 into develop Jul 21, 2026
7 checks passed
@davidp57
davidp57 deleted the feature/ctld-tools-tui-polish branch July 21, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant