mfgparams 2.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mfgparams/__init__.py +72 -0
- mfgparams/__main__.py +386 -0
- mfgparams/config.py +112 -0
- mfgparams/console/__init__.py +19 -0
- mfgparams/console/__main__.py +22 -0
- mfgparams/console/cli.py +97 -0
- mfgparams/console/i18n.py +136 -0
- mfgparams/console/locales/__init__.py +26 -0
- mfgparams/console/locales/en.py +203 -0
- mfgparams/console/tui/__init__.py +9 -0
- mfgparams/console/tui/app.py +1540 -0
- mfgparams/console/tui/forms.py +245 -0
- mfgparams/console/tui/machining_menu.py +102 -0
- mfgparams/console/tui/material_picker.py +330 -0
- mfgparams/console/tui/menu.py +110 -0
- mfgparams/console/tui/screens/__init__.py +3 -0
- mfgparams/console/tui/screens/about.py +24 -0
- mfgparams/console/tui/screens/configuration.py +118 -0
- mfgparams/console/tui/screens/drilling.py +298 -0
- mfgparams/console/tui/screens/help.py +19 -0
- mfgparams/console/tui/screens/milling.py +468 -0
- mfgparams/console/tui/screens/split_pane.py +476 -0
- mfgparams/console/tui/screens/turning.py +340 -0
- mfgparams/console/tui/terminal_capability.py +65 -0
- mfgparams/data/__init__.py +8 -0
- mfgparams/data/materials.toml +175 -0
- mfgparams/i18n.py +151 -0
- mfgparams/locales/__init__.py +14 -0
- mfgparams/locales/en.py +161 -0
- mfgparams/logging_setup.py +34 -0
- mfgparams/models.py +283 -0
- mfgparams/processes/__init__.py +21 -0
- mfgparams/processes/machining/__init__.py +20 -0
- mfgparams/processes/machining/drilling/__init__.py +524 -0
- mfgparams/processes/machining/drilling/data/__init__.py +8 -0
- mfgparams/processes/machining/drilling/data/tools.toml +28 -0
- mfgparams/processes/machining/drilling/formulas.py +175 -0
- mfgparams/processes/machining/drilling/tools.py +152 -0
- mfgparams/processes/machining/milling/__init__.py +13 -0
- mfgparams/processes/machining/milling/_calculate.py +638 -0
- mfgparams/processes/machining/milling/_shared.py +342 -0
- mfgparams/processes/machining/milling/_tool_registry.py +151 -0
- mfgparams/processes/machining/milling/end_milling/__init__.py +183 -0
- mfgparams/processes/machining/milling/end_milling/data/__init__.py +8 -0
- mfgparams/processes/machining/milling/end_milling/data/tools.toml +40 -0
- mfgparams/processes/machining/milling/end_milling/formulas.py +154 -0
- mfgparams/processes/machining/milling/end_milling/tools.py +71 -0
- mfgparams/processes/machining/milling/face_milling/__init__.py +186 -0
- mfgparams/processes/machining/milling/face_milling/data/__init__.py +8 -0
- mfgparams/processes/machining/milling/face_milling/data/tools.toml +36 -0
- mfgparams/processes/machining/milling/face_milling/formulas.py +156 -0
- mfgparams/processes/machining/milling/face_milling/tools.py +68 -0
- mfgparams/processes/machining/turning/__init__.py +779 -0
- mfgparams/processes/machining/turning/data/__init__.py +1 -0
- mfgparams/processes/machining/turning/data/tools.toml +32 -0
- mfgparams/processes/machining/turning/formulas.py +497 -0
- mfgparams/processes/machining/turning/tools.py +143 -0
- mfgparams/registry.py +473 -0
- mfgparams/registry_config.py +450 -0
- mfgparams/units.py +197 -0
- mfgparams/validation.py +718 -0
- mfgparams-2.5.0.dist-info/METADATA +703 -0
- mfgparams-2.5.0.dist-info/RECORD +67 -0
- mfgparams-2.5.0.dist-info/WHEEL +5 -0
- mfgparams-2.5.0.dist-info/entry_points.txt +2 -0
- mfgparams-2.5.0.dist-info/licenses/LICENSE.md +89 -0
- mfgparams-2.5.0.dist-info/top_level.txt +1 -0
mfgparams/console/cli.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Console entry point: launches the text GUI, or exits with a clear message
|
|
2
|
+
if this terminal cannot support it (specs/017-console-text-gui).
|
|
3
|
+
|
|
4
|
+
The REPL this module used to hold is deleted entirely (FR-001, FR-007) --
|
|
5
|
+
not hidden behind a flag, not kept for scripting. See spec.md's "Entry-point
|
|
6
|
+
precedence" note in Context for why, and contracts/console-tui-contract.md
|
|
7
|
+
§1 for the full entry-point contract.
|
|
8
|
+
|
|
9
|
+
`main()` here is the FR-006/User Story 3 negative-path implementation
|
|
10
|
+
itself, not a stub deferred elsewhere (/speckit-analyze finding F2): it
|
|
11
|
+
calls `terminal_capability.check()` (research.md #2) *before* importing or
|
|
12
|
+
constructing anything prompt-toolkit-related, and exits via the catalog
|
|
13
|
+
message on failure.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
from mfgparams.console.tui import terminal_capability
|
|
22
|
+
from mfgparams.i18n import get_locale, translate
|
|
23
|
+
from mfgparams.logging_setup import configure_logging
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _report_unsupported_terminal(capability: terminal_capability.TerminalCapability) -> int:
|
|
27
|
+
"""Print the FR-006 message and return the exit status."""
|
|
28
|
+
|
|
29
|
+
locale = get_locale()
|
|
30
|
+
if not capability.has_tty:
|
|
31
|
+
reason = translate(locale, "console.tui_unavailable.reason.no_tty")
|
|
32
|
+
else:
|
|
33
|
+
reason = translate(
|
|
34
|
+
locale,
|
|
35
|
+
"console.tui_unavailable.reason.too_small",
|
|
36
|
+
columns=capability.columns,
|
|
37
|
+
lines=capability.lines,
|
|
38
|
+
min_columns=terminal_capability.MIN_COLUMNS,
|
|
39
|
+
min_lines=terminal_capability.MIN_LINES,
|
|
40
|
+
)
|
|
41
|
+
message = translate(
|
|
42
|
+
locale,
|
|
43
|
+
"console.tui_unavailable",
|
|
44
|
+
reason=reason,
|
|
45
|
+
min_columns=terminal_capability.MIN_COLUMNS,
|
|
46
|
+
min_lines=terminal_capability.MIN_LINES,
|
|
47
|
+
)
|
|
48
|
+
print(message, file=sys.stderr)
|
|
49
|
+
return 1
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
53
|
+
"""Parse CLI arguments (currently just ``--materials-config``)."""
|
|
54
|
+
|
|
55
|
+
parser = argparse.ArgumentParser(prog="mfgparams")
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--materials-config",
|
|
58
|
+
dest="materials_config",
|
|
59
|
+
default=None,
|
|
60
|
+
metavar="PATH",
|
|
61
|
+
help=(
|
|
62
|
+
"Optional path to a TOML file adding/overriding materials and "
|
|
63
|
+
"drilling tools (see contracts/materials-config-schema.md)."
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
return parser.parse_args(argv)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main() -> int:
|
|
70
|
+
"""Console-script entry point (``mfgparams`` / ``python -m mfgparams``).
|
|
71
|
+
|
|
72
|
+
Returns the process exit status: ``1`` if this terminal cannot support
|
|
73
|
+
the text GUI (FR-006), otherwise whatever the text GUI returns once it
|
|
74
|
+
has run to completion (``0`` when it returns nothing).
|
|
75
|
+
|
|
76
|
+
Argument parsing runs *before* the terminal-capability gate, not after:
|
|
77
|
+
``--help``/``-h`` (and an invalid argument) must work over a pipe like
|
|
78
|
+
any ordinary CLI tool's does -- `argparse` exits on its own for both
|
|
79
|
+
before this function would otherwise reach the gate, and gating first
|
|
80
|
+
would incorrectly block a `--help` request piped through `| less` or a
|
|
81
|
+
CI script with the FR-006 message instead of the help text.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
configure_logging()
|
|
85
|
+
args = _parse_args()
|
|
86
|
+
|
|
87
|
+
capability = terminal_capability.check()
|
|
88
|
+
if not capability.supported:
|
|
89
|
+
return _report_unsupported_terminal(capability)
|
|
90
|
+
|
|
91
|
+
from mfgparams.console.tui.app import run
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
run(materials_config_path=args.materials_config)
|
|
95
|
+
except (KeyboardInterrupt, EOFError):
|
|
96
|
+
print()
|
|
97
|
+
return 0
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Message catalog loader and locale resolution for the console.
|
|
2
|
+
|
|
3
|
+
Mirrors :mod:`mfgparams.i18n` exactly in structure and contract, scoped to
|
|
4
|
+
:mod:`mfgparams.console.locales` instead of :mod:`mfgparams.locales`
|
|
5
|
+
(specs/015-console-i18n-relocation research.md #1 — a deliberate duplicate,
|
|
6
|
+
not a shared module, because the two catalogs have different compatibility
|
|
7
|
+
contracts: core's is public-API surface, console's is presentation and can
|
|
8
|
+
change freely).
|
|
9
|
+
|
|
10
|
+
Provides:
|
|
11
|
+
|
|
12
|
+
- :func:`get_locale`: reads ``MFGPARAMS_LOCALE``, resolved against the
|
|
13
|
+
console's own bundled catalogs. Callers that need a *fixed-for-session*
|
|
14
|
+
locale (the console itself) MUST call this exactly once at startup and
|
|
15
|
+
hold the result for the lifetime of that session.
|
|
16
|
+
- :func:`translate`: looks up a message by ID for a given locale, falling
|
|
17
|
+
back to the console's English catalog for any missing locale or key, and
|
|
18
|
+
substituting ``str.format()``-style named placeholders. Never raises to
|
|
19
|
+
the caller — a missing placeholder value is logged (in English) rather
|
|
20
|
+
than surfaced as an exception.
|
|
21
|
+
|
|
22
|
+
This module intentionally contains no user-facing English text itself; all
|
|
23
|
+
strings live in ``mfgparams.console.locales.*`` catalog modules.
|
|
24
|
+
|
|
25
|
+
Does **not** duplicate :func:`mfgparams.i18n.get_raw_locale`: that function
|
|
26
|
+
has no catalog dependency (it resolves ``MFGPARAMS_LOCALE`` verbatim, for
|
|
27
|
+
data-driven lookups such as ``WorkpieceMaterial.translations``) and stays a
|
|
28
|
+
``mfgparams.i18n`` import wherever it is used — those lookups are Story 3 of
|
|
29
|
+
specs/015-console-i18n-relocation and are untouched by this catalog split.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import importlib
|
|
35
|
+
import logging
|
|
36
|
+
import os
|
|
37
|
+
|
|
38
|
+
DEFAULT_LOCALE = "en"
|
|
39
|
+
_LOCALE_ENV_VAR = "MFGPARAMS_LOCALE"
|
|
40
|
+
|
|
41
|
+
_logger = logging.getLogger(__name__)
|
|
42
|
+
|
|
43
|
+
# Cache of locale -> catalog dict (or None if the locale has no bundled
|
|
44
|
+
# module). A plain dict (rather than functools.lru_cache) so tests can
|
|
45
|
+
# register/clear fixture catalogs deterministically.
|
|
46
|
+
_catalog_cache: dict[str, dict[str, str] | None] = {}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _load_catalog(locale: str) -> dict[str, str] | None:
|
|
50
|
+
"""Load the ``MESSAGES`` dict from ``mfgparams.console.locales.<locale>``.
|
|
51
|
+
|
|
52
|
+
Returns ``None`` if no such bundled locale module exists. Results are
|
|
53
|
+
cached per locale for the process lifetime.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
if locale in _catalog_cache:
|
|
57
|
+
return _catalog_cache[locale]
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
module = importlib.import_module(f"mfgparams.console.locales.{locale}")
|
|
61
|
+
except ImportError:
|
|
62
|
+
_catalog_cache[locale] = None
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
catalog = getattr(module, "MESSAGES", None)
|
|
66
|
+
_catalog_cache[locale] = catalog
|
|
67
|
+
return catalog
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def clear_catalog_cache() -> None:
|
|
71
|
+
"""Clear the locale catalog cache (test support only)."""
|
|
72
|
+
|
|
73
|
+
_catalog_cache.clear()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_locale() -> str:
|
|
77
|
+
"""Resolve the active locale from ``MFGPARAMS_LOCALE``, against the
|
|
78
|
+
console's own bundled catalogs.
|
|
79
|
+
|
|
80
|
+
Returns the environment variable's value if it is non-empty and matches
|
|
81
|
+
a bundled locale module; otherwise returns :data:`DEFAULT_LOCALE`
|
|
82
|
+
(``"en"``). No OS/system locale (``LANG``/``LC_ALL``) auto-detection is
|
|
83
|
+
performed. The console MUST call this function exactly once per session
|
|
84
|
+
and hold the result, exactly as :func:`mfgparams.i18n.get_locale`
|
|
85
|
+
requires of its own callers.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
raw = os.environ.get(_LOCALE_ENV_VAR, "")
|
|
89
|
+
if raw and _load_catalog(raw) is not None:
|
|
90
|
+
return raw
|
|
91
|
+
return DEFAULT_LOCALE
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def has_message(locale: str, key: str) -> bool:
|
|
95
|
+
"""Report whether ``key`` resolves to a real catalog entry.
|
|
96
|
+
|
|
97
|
+
Uses the same lookup order as :func:`translate` (``locale`` first, then
|
|
98
|
+
the English catalog) but stops before formatting. See
|
|
99
|
+
:func:`mfgparams.i18n.has_message`'s docstring for why this is needed
|
|
100
|
+
rather than comparing ``translate``'s result against the key.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
catalog = _load_catalog(locale)
|
|
104
|
+
if catalog is not None and key in catalog:
|
|
105
|
+
return True
|
|
106
|
+
english = _load_catalog(DEFAULT_LOCALE) or {}
|
|
107
|
+
return key in english
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def translate(locale: str, key: str, **kwargs: object) -> str:
|
|
111
|
+
"""Look up and format message ``key`` for ``locale``.
|
|
112
|
+
|
|
113
|
+
Falls back to the console's English catalog if ``locale`` has no
|
|
114
|
+
bundled module or lacks ``key``. If a placeholder required by the
|
|
115
|
+
message template is missing from ``kwargs``, this function does NOT
|
|
116
|
+
raise; instead it logs a warning (in English) and returns the
|
|
117
|
+
unformatted template as a usable fallback.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
catalog = _load_catalog(locale)
|
|
121
|
+
template = catalog.get(key) if catalog else None
|
|
122
|
+
|
|
123
|
+
if template is None:
|
|
124
|
+
english = _load_catalog(DEFAULT_LOCALE) or {}
|
|
125
|
+
template = english.get(key, key)
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
return template.format(**kwargs)
|
|
129
|
+
except (KeyError, IndexError, ValueError):
|
|
130
|
+
_logger.warning(
|
|
131
|
+
"i18n: missing or invalid placeholder value for message key %r "
|
|
132
|
+
"(locale=%r); returning unformatted template",
|
|
133
|
+
key,
|
|
134
|
+
locale,
|
|
135
|
+
)
|
|
136
|
+
return template
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Message catalogs owned by the console, one pure-Python module per locale.
|
|
2
|
+
|
|
3
|
+
Mirrors :mod:`mfgparams.locales`'s shape exactly (each sibling module exposes
|
|
4
|
+
a module-level ``MESSAGES: dict[str, str]``), but a separate package: this
|
|
5
|
+
catalog holds UI strings (prompts, labels, status/progress text) the console
|
|
6
|
+
displays, while :mod:`mfgparams.locales` keeps only the entries that must
|
|
7
|
+
remain reachable without the ``console`` extra installed — error text
|
|
8
|
+
(``error.*``/``warning.*``/``notice.*``) and the console-unavailable message
|
|
9
|
+
(``console.missing_dependency*``) — per
|
|
10
|
+
specs/015-console-i18n-relocation/contracts/catalogue-ownership-contract.md.
|
|
11
|
+
|
|
12
|
+
A key lives in exactly one of the two catalogs — with two documented
|
|
13
|
+
exceptions (FR-004): ``console.missing_dependency*`` stays core-only, and
|
|
14
|
+
``cli.label.axial_depth_of_cut``/``radial_depth_of_cut``/``width_of_cut``
|
|
15
|
+
live in **both**, because core embeds one of them inside its own English
|
|
16
|
+
error text (found during T007; see
|
|
17
|
+
specs/015-console-i18n-relocation/contracts/catalogue-ownership-contract.md
|
|
18
|
+
for the full reasoning on both exceptions). Outside those two, moving an
|
|
19
|
+
entry here removes it from :mod:`mfgparams.locales` in the same change.
|
|
20
|
+
|
|
21
|
+
This feature ships only :mod:`mfgparams.console.locales.en`; additional
|
|
22
|
+
languages can be added later by contributing a new sibling module of the
|
|
23
|
+
same shape, without any code changes elsewhere.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""English (``en``) message catalog for the console — the default and fallback locale.
|
|
2
|
+
|
|
3
|
+
Message IDs are stable and language-independent; do not rename an existing
|
|
4
|
+
key when editing its English text (other locale modules and tests key off
|
|
5
|
+
these IDs). See ``specs/015-console-i18n-relocation/data-model.md`` for the
|
|
6
|
+
entity definition this module implements, and
|
|
7
|
+
``specs/017-console-text-gui/contracts/console-tui-contract.md`` §4 for which
|
|
8
|
+
strings belong here versus in :mod:`mfgparams.locales.en`.
|
|
9
|
+
|
|
10
|
+
specs/017-console-text-gui replaces the REPL with a full-screen text GUI: the
|
|
11
|
+
``cli.*`` keys this catalog held for the REPL are retired along with the REPL
|
|
12
|
+
code that used them, replaced by the ``tui.*`` keys below (contracts §4 --
|
|
13
|
+
new keys introduced by that feature are namespaced ``tui.*``).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
MESSAGES: dict[str, str] = {
|
|
19
|
+
# --- Top-level menu and Machining submenu (FR-009, FR-010) ---
|
|
20
|
+
"tui.menu.title": "mfgparams",
|
|
21
|
+
"tui.menu.hint": "Arrow keys or the highlighted letter to choose, Enter to select.",
|
|
22
|
+
"tui.menu.machining": "Machining",
|
|
23
|
+
"tui.menu.configuration": "Configuration",
|
|
24
|
+
"tui.menu.about": "About",
|
|
25
|
+
"tui.menu.help": "Help",
|
|
26
|
+
# 018-tui-splitpane-redesign FR-001: new to the persistent bar -- 017 had
|
|
27
|
+
# no labeled Exit item, only an unlabeled Escape/Ctrl-Q handler.
|
|
28
|
+
"tui.menu.exit": "Exit",
|
|
29
|
+
# Exit confirmation dialog (revision, per direct user feedback): shown
|
|
30
|
+
# as a floating Yes/No prompt when Exit is selected from the bar,
|
|
31
|
+
# rather than exiting immediately.
|
|
32
|
+
"tui.exit_confirm.message": "Are you sure you want to exit?",
|
|
33
|
+
"tui.exit_confirm.yes": "Yes",
|
|
34
|
+
"tui.exit_confirm.no": "No",
|
|
35
|
+
"tui.machining_menu.title": "Machining",
|
|
36
|
+
"tui.machining_menu.milling": "Milling",
|
|
37
|
+
"tui.machining_menu.drilling": "Drilling",
|
|
38
|
+
"tui.machining_menu.turning": "Turning",
|
|
39
|
+
# --- Shared dialog chrome ---
|
|
40
|
+
"tui.action.ok": "OK",
|
|
41
|
+
"tui.action.back": "Back",
|
|
42
|
+
"tui.error.title": "Invalid input",
|
|
43
|
+
# --- Unit system / calculation mode (shared by Milling and Drilling) ---
|
|
44
|
+
"tui.label.unit_system": "Unit system",
|
|
45
|
+
"tui.unit_system.metric": "metric",
|
|
46
|
+
"tui.unit_system.imperial": "imperial",
|
|
47
|
+
"tui.label.mode": "Calculation mode",
|
|
48
|
+
"tui.mode.standard": "standard",
|
|
49
|
+
"tui.mode.power_constrained": "power-constrained",
|
|
50
|
+
"tui.mode.fixed_rpm": "fixed-rpm",
|
|
51
|
+
"tui.mode.feed_rate_constrained": "feed-rate-constrained",
|
|
52
|
+
"tui.mode.rotation_and_feed_constrained": "rotation-and-feed-constrained",
|
|
53
|
+
"tui.mode.power_and_feed_constrained": "power-and-feed-constrained",
|
|
54
|
+
"tui.label.power": "Available power",
|
|
55
|
+
"tui.label.power_required": "Available power",
|
|
56
|
+
"tui.prompt.power_required.invalid": (
|
|
57
|
+
"Please enter a positive numeric value for available power."
|
|
58
|
+
),
|
|
59
|
+
"tui.label.target_rpm": "Target spindle speed",
|
|
60
|
+
"tui.label.target_feed_rate": "Feed rate per rotation",
|
|
61
|
+
"tui.prompt.target_rpm.invalid": (
|
|
62
|
+
"Please enter a positive numeric value for target spindle speed."
|
|
63
|
+
),
|
|
64
|
+
# --- Material / tool selection (shared by Milling and Drilling) ---
|
|
65
|
+
"tui.label.material_type": "Material type",
|
|
66
|
+
"material_type.metal": "Metal",
|
|
67
|
+
"material_type.wood": "Wood",
|
|
68
|
+
"material_type.uncategorized": "Uncategorized",
|
|
69
|
+
"tui.label.material": "Material",
|
|
70
|
+
"tui.label.tool": "Drilling tool",
|
|
71
|
+
"tui.label.unit_system_suffix": "{name} [{unit_system}]",
|
|
72
|
+
# --- Material selection window (specs/023-material-selector-dialog) ---
|
|
73
|
+
"tui.material_picker.title": "Select material",
|
|
74
|
+
"tui.material_picker.column_common": "Name",
|
|
75
|
+
"tui.material_picker.column_number": "Material No.",
|
|
76
|
+
"tui.material_picker.column_short": "Short",
|
|
77
|
+
"tui.material_picker.empty": "No matching materials.",
|
|
78
|
+
"tui.material_picker.pane_hint": ("↑↓ move ←→/Space change ↵ detailed search Esc back"),
|
|
79
|
+
# --- Drilling form (FR-002) ---
|
|
80
|
+
"tui.drilling.title": "Drilling",
|
|
81
|
+
"tui.label.diameter": "Drill diameter",
|
|
82
|
+
"tui.label.depth": "Hole depth",
|
|
83
|
+
# 018-tui-splitpane-redesign FR-006 (revision): the right pane's
|
|
84
|
+
# placeholder while required inputs are incomplete -- operation-specific
|
|
85
|
+
# wording, matching the prototype's own `_last_result_text` default.
|
|
86
|
+
"tui.drilling.placeholder": "Enter a diameter and a hole depth to see a result.",
|
|
87
|
+
# --- Turning form (specs/019-turning-calculations) ---
|
|
88
|
+
"tui.turning.title": "Turning",
|
|
89
|
+
"tui.label.workpiece_diameter": "Workpiece diameter",
|
|
90
|
+
"tui.label.depth_of_cut": "Depth of cut",
|
|
91
|
+
"tui.label.turning_tool": "Turning tool",
|
|
92
|
+
"tui.turning.placeholder": (
|
|
93
|
+
"Enter a workpiece diameter, depth of cut, and length of cut to see a result."
|
|
94
|
+
),
|
|
95
|
+
# --- Milling form (FR-002) ---
|
|
96
|
+
"tui.milling.title": "Milling",
|
|
97
|
+
"tui.milling.placeholder": (
|
|
98
|
+
"Enter diameter, depth of cut, engagement, feed, teeth, and length of cut to see a result."
|
|
99
|
+
),
|
|
100
|
+
"tui.label.milling_sub_operation": "Milling operation",
|
|
101
|
+
"tui.milling_sub_operation.end_milling": "end milling",
|
|
102
|
+
"tui.milling_sub_operation.face_milling": "face milling",
|
|
103
|
+
"tui.label.mill_diameter": "Cutter diameter",
|
|
104
|
+
"tui.label.end_mill_tool": "End-mill tool",
|
|
105
|
+
"tui.label.face_mill_tool": "Face-mill tool",
|
|
106
|
+
# These three are deliberately NOT tui.*-namespaced (contracts §4's
|
|
107
|
+
# dual-presence exception, unchanged from the REPL): core's
|
|
108
|
+
# validate_depth_of_cut_mm/validate_engagement_mm embed a label inside
|
|
109
|
+
# English error text by this exact key name (mfgparams.locales.en carries
|
|
110
|
+
# the same three, untouched by this feature), and this module's own
|
|
111
|
+
# error-rendering must resolve the identical key to re-translate it. The
|
|
112
|
+
# TUI's own field labels reuse them rather than inventing tui.* aliases,
|
|
113
|
+
# so there is exactly one key per concept, not two that could drift.
|
|
114
|
+
"cli.label.axial_depth_of_cut": "Axial depth of cut",
|
|
115
|
+
"cli.label.radial_depth_of_cut": "Radial depth of cut",
|
|
116
|
+
"cli.label.width_of_cut": "Width of cut",
|
|
117
|
+
"tui.label.feed_per_tooth": "Feed per tooth",
|
|
118
|
+
"tui.label.number_of_teeth": "Number of teeth",
|
|
119
|
+
"tui.label.length_of_cut": "Length of cut",
|
|
120
|
+
"tui.unit.teeth": "teeth",
|
|
121
|
+
# --- Numeric input prompt/validation (shared) ---
|
|
122
|
+
"tui.prompt.number": "{label} ({unit})",
|
|
123
|
+
"tui.prompt.number.with_default": "{label} ({unit}, default {default})",
|
|
124
|
+
"tui.prompt.power.optional_hint": "Leave blank if unknown.",
|
|
125
|
+
# 018-tui-splitpane-redesign, revision (matching the pre-plan prototype
|
|
126
|
+
# exactly): FR-006b's unparseable-number message now surfaces in the
|
|
127
|
+
# bottom status bar, only once the user tries to navigate away from the
|
|
128
|
+
# offending field -- not proactively while still typing (split_pane.py's
|
|
129
|
+
# `_commit_current`).
|
|
130
|
+
"tui.validation.unparseable_number": "'{text}' is not a number -- kept previous value.",
|
|
131
|
+
# The left pane's own title, distinct from the floating window's own
|
|
132
|
+
# title (the operation name -- FR-004, revision) -- matches the
|
|
133
|
+
# prototype's `render_left`.
|
|
134
|
+
"tui.pane.inputs": "Inputs",
|
|
135
|
+
# The bottom status/hint row shown whenever no `OperationScreen.status`
|
|
136
|
+
# message is pending (revision, matching the prototype's
|
|
137
|
+
# `render_bottom`) -- adapted from its literal "Esc/^Q quit" for this
|
|
138
|
+
# app's real semantics: Escape here returns focus to the persistent
|
|
139
|
+
# menu bar, not necessarily exiting the whole application.
|
|
140
|
+
"tui.pane.hint": "↑↓ move ←→/Space change Esc back",
|
|
141
|
+
# --- Result display ---
|
|
142
|
+
# Reused unchanged as the right pane's title (018-tui-splitpane-redesign
|
|
143
|
+
# FR-006/FR-006a) -- "Result" reads correctly whether it labels a modal
|
|
144
|
+
# dialog (017) or a persistent pane (018).
|
|
145
|
+
"tui.result.title": "Result",
|
|
146
|
+
"tui.result.spindle_speed": "Spindle speed: {value} RPM{mode_suffix}",
|
|
147
|
+
"tui.result.spindle_speed.mode_suffix": " ({label})",
|
|
148
|
+
"tui.result.spindle_speed.mode.standard": "recommended",
|
|
149
|
+
"tui.result.spindle_speed.mode.power_constrained": "adjusted to fit available power",
|
|
150
|
+
"tui.result.spindle_speed.mode.fixed_rpm": "user-specified",
|
|
151
|
+
"tui.result.spindle_speed.mode.feed_rate_constrained": "derived from cutting speed",
|
|
152
|
+
"tui.result.feed_rate": "Feed rate: {value} {unit}",
|
|
153
|
+
"tui.result.feed_per_rotation": "Feed per rotation: {value} {unit}",
|
|
154
|
+
"tui.result.machining_time": "Machining time: {value} min",
|
|
155
|
+
"tui.result.torque": "Torque: {value} {unit}",
|
|
156
|
+
"tui.result.power_required": "Power required: {value} {unit}",
|
|
157
|
+
"tui.result.material_removal_rate": "Material removal: {value} {unit}",
|
|
158
|
+
"tui.result.cutting_force": "Cutting force: {value} {unit}",
|
|
159
|
+
"tui.result.warning": "\nWarning: {message}",
|
|
160
|
+
"tui.result.error.title": "Calculation error",
|
|
161
|
+
# --- Configuration screen (read-only, research.md #4) ---
|
|
162
|
+
"tui.configuration.title": "Configuration",
|
|
163
|
+
"tui.configuration.path_label.set": "Configuration file: {path}",
|
|
164
|
+
"tui.configuration.path_label.default": "Configuration file: (bundled defaults)",
|
|
165
|
+
"tui.configuration.section.material_types": "Material types: {items}",
|
|
166
|
+
"tui.configuration.section.materials": "Materials ({material_type}): {items}",
|
|
167
|
+
"tui.configuration.section.tools": "Drilling tools: {items}",
|
|
168
|
+
# 018-tui-splitpane-redesign FR-015: closes the gap PR #94's review found
|
|
169
|
+
# (Configuration only loaded the drilling registry) -- same screen being
|
|
170
|
+
# rebuilt for this feature, don't patch it twice.
|
|
171
|
+
"tui.configuration.section.end_mill_tools": "End-mill tools: {items}",
|
|
172
|
+
"tui.configuration.section.face_mill_tools": "Face-mill tools: {items}",
|
|
173
|
+
# specs/019-turning-calculations: same completeness requirement PR #94's
|
|
174
|
+
# review found for milling -- every tool registry must be listed here.
|
|
175
|
+
"tui.configuration.section.turning_tools": "Turning tools: {items}",
|
|
176
|
+
# --- About screen ---
|
|
177
|
+
"tui.about.title": "About",
|
|
178
|
+
"tui.about.text": (
|
|
179
|
+
"{name} version {version}\n\nLicensed under the PolyForm Noncommercial License 1.0.0.\n"
|
|
180
|
+
"See LICENSE.md for details."
|
|
181
|
+
),
|
|
182
|
+
# --- Help screen (placeholder, FR-009) ---
|
|
183
|
+
"tui.help.title": "Help",
|
|
184
|
+
"tui.help.text": (
|
|
185
|
+
"Use the arrow keys and Enter to navigate, or the highlighted letter shown "
|
|
186
|
+
"on a menu item to jump to it directly. Press Tab to move between a screen's "
|
|
187
|
+
"fields and its buttons. Choose Back to return to the previous screen."
|
|
188
|
+
),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# specs/021-turning-combined-constraints research.md #8: these two modes'
|
|
192
|
+
# spindle-speed labels genuinely reuse an existing mode's value (the
|
|
193
|
+
# spindle speed's own derivation nature is identical, even though the feed
|
|
194
|
+
# value's source differs) -- assigned by reference to the existing key
|
|
195
|
+
# here, rather than duplicated as a second literal string inside the dict
|
|
196
|
+
# above, so a future wording change to either source value can't silently
|
|
197
|
+
# leave these two out of sync (Copilot review finding on PR #102).
|
|
198
|
+
MESSAGES["tui.result.spindle_speed.mode.rotation_and_feed_constrained"] = MESSAGES[
|
|
199
|
+
"tui.result.spindle_speed.mode.fixed_rpm"
|
|
200
|
+
]
|
|
201
|
+
MESSAGES["tui.result.spindle_speed.mode.power_and_feed_constrained"] = MESSAGES[
|
|
202
|
+
"tui.result.spindle_speed.mode.power_constrained"
|
|
203
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Full-screen text GUI for ``mfgparams.console`` (specs/017-console-text-gui).
|
|
2
|
+
|
|
3
|
+
Replaces the line-based REPL as the sole interactive entry point. Built on
|
|
4
|
+
prompt-toolkit (confirmed by that feature's technical spike). Contains no
|
|
5
|
+
calculation logic of its own -- every result comes from the public
|
|
6
|
+
``mfgparams`` calculation API, exactly as the REPL it replaces did.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|