python-color-math 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {python_color_math-0.2.0/python_color_math.egg-info → python_color_math-0.2.1}/PKG-INFO +4 -2
- {python_color_math-0.2.0 → python_color_math-0.2.1}/README.md +1 -1
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/__init__.py +1 -1
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/config.py +56 -4
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/block.py +15 -5
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/derivative.py +3 -3
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/generic.py +34 -12
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/matrix.py +5 -3
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/main.py +16 -1
- python_color_math-0.2.1/color_math/mcp.py +42 -0
- python_color_math-0.2.1/color_math/mcp_server.py +425 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/braket.py +20 -21
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/differentials.py +12 -9
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/dimensionless.py +7 -8
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/latex_spans.py +6 -13
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/markdown_scanner.py +50 -3
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/math_parser.py +18 -17
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/scanner.py +2 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/taxonomy.py +11 -16
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/units.py +32 -30
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/variable_hash.py +13 -19
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/undo.py +15 -7
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/utils/latex_helpers.py +89 -8
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/utils/spans.py +61 -5
- {python_color_math-0.2.0 → python_color_math-0.2.1}/pyproject.toml +5 -1
- {python_color_math-0.2.0 → python_color_math-0.2.1/python_color_math.egg-info}/PKG-INFO +4 -2
- {python_color_math-0.2.0 → python_color_math-0.2.1}/python_color_math.egg-info/SOURCES.txt +8 -1
- {python_color_math-0.2.0 → python_color_math-0.2.1}/python_color_math.egg-info/entry_points.txt +1 -0
- python_color_math-0.2.1/python_color_math.egg-info/requires.txt +3 -0
- python_color_math-0.2.1/tests/test_adversarial.py +207 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/tests/test_features.py +59 -0
- python_color_math-0.2.1/tests/test_fuzz.py +218 -0
- python_color_math-0.2.1/tests/test_mcp_hardcore.py +411 -0
- python_color_math-0.2.1/tests/test_mcp_server.py +116 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/.pre-commit-hooks.yaml +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/LICENSE +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/MANIFEST.in +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/__main__.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/adapters.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/completions.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/__init__.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/align.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/equation.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/integral.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/limit.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/converters/semantic.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/gui.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/io.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/__init__.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/alignment.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/constants.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/parsers/delimiters.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/py.typed +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/self_test.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/tutorial.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/utils/__init__.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/color_math/utils/coloring.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/python_color_math/__init__.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/python_color_math/__main__.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/python_color_math.egg-info/dependency_links.txt +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/python_color_math.egg-info/top_level.txt +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/setup.cfg +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/tests/test_cli.py +0 -0
- {python_color_math-0.2.0 → python_color_math-0.2.1}/tests/test_gui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-color-math
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: A fast, local semantic LaTeX colorizer for Markdown, Jupyter, and LaTeX math equations.
|
|
5
5
|
Author: Aditya
|
|
6
6
|
License: MIT License
|
|
@@ -51,6 +51,8 @@ Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
|
51
51
|
Requires-Python: >=3.10
|
|
52
52
|
Description-Content-Type: text/markdown
|
|
53
53
|
License-File: LICENSE
|
|
54
|
+
Provides-Extra: mcp
|
|
55
|
+
Requires-Dist: mcp>=1.0.0; extra == "mcp"
|
|
54
56
|
Dynamic: license-file
|
|
55
57
|
|
|
56
58
|
# 🎨 Python Color Math
|
|
@@ -230,7 +232,7 @@ Integrate `color-math` into your git workflow using [pre-commit](https://pre-com
|
|
|
230
232
|
```yaml
|
|
231
233
|
repos:
|
|
232
234
|
- repo: https://github.com/36ty-blip/color-math
|
|
233
|
-
rev: v0.2.
|
|
235
|
+
rev: v0.2.1
|
|
234
236
|
hooks:
|
|
235
237
|
# Formatter: colorizes modified Markdown, LaTeX, and Jupyter files in-place
|
|
236
238
|
- id: color-math
|
|
@@ -175,7 +175,7 @@ Integrate `color-math` into your git workflow using [pre-commit](https://pre-com
|
|
|
175
175
|
```yaml
|
|
176
176
|
repos:
|
|
177
177
|
- repo: https://github.com/36ty-blip/color-math
|
|
178
|
-
rev: v0.2.
|
|
178
|
+
rev: v0.2.1
|
|
179
179
|
hooks:
|
|
180
180
|
# Formatter: colorizes modified Markdown, LaTeX, and Jupyter files in-place
|
|
181
181
|
- id: color-math
|
|
@@ -272,7 +272,7 @@ FONT_STYLE_MACROS = frozenset({
|
|
|
272
272
|
})
|
|
273
273
|
|
|
274
274
|
|
|
275
|
-
|
|
275
|
+
STANDARD_BARE_FUNCTIONS = frozenset({
|
|
276
276
|
"sin",
|
|
277
277
|
"cos",
|
|
278
278
|
"tan",
|
|
@@ -313,6 +313,9 @@ BARE_FUNCTIONS = frozenset({
|
|
|
313
313
|
"span",
|
|
314
314
|
"diag",
|
|
315
315
|
"sgn",
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
EXTENDED_BARE_FUNCTIONS = frozenset({
|
|
316
319
|
"adj",
|
|
317
320
|
"col",
|
|
318
321
|
"row",
|
|
@@ -349,15 +352,40 @@ BARE_FUNCTIONS = frozenset({
|
|
|
349
352
|
"lg",
|
|
350
353
|
"lb",
|
|
351
354
|
"aut",
|
|
352
|
-
"end",
|
|
353
355
|
"gal",
|
|
354
356
|
"ann",
|
|
355
357
|
"tor",
|
|
356
358
|
"ext",
|
|
357
359
|
"pic",
|
|
358
360
|
"cl",
|
|
361
|
+
"jac",
|
|
362
|
+
"hes",
|
|
363
|
+
"wr",
|
|
364
|
+
"vol",
|
|
365
|
+
"rms",
|
|
366
|
+
"fft",
|
|
367
|
+
"dft",
|
|
368
|
+
"ord",
|
|
369
|
+
"val",
|
|
370
|
+
"num",
|
|
371
|
+
"den",
|
|
372
|
+
"sn",
|
|
373
|
+
"cn",
|
|
374
|
+
"dn",
|
|
375
|
+
"avg",
|
|
376
|
+
"len",
|
|
359
377
|
})
|
|
360
378
|
|
|
379
|
+
FULL_BARE_FUNCTIONS = STANDARD_BARE_FUNCTIONS | EXTENDED_BARE_FUNCTIONS
|
|
380
|
+
ALL_BARE_FUNCTIONS = FULL_BARE_FUNCTIONS
|
|
381
|
+
BARE_FUNCTIONS = FULL_BARE_FUNCTIONS
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def get_bare_functions(options: ColorMathOptions | None = None) -> frozenset[str]:
|
|
385
|
+
if options is not None and not options.extended_functions:
|
|
386
|
+
return STANDARD_BARE_FUNCTIONS
|
|
387
|
+
return FULL_BARE_FUNCTIONS
|
|
388
|
+
|
|
361
389
|
|
|
362
390
|
MATH_ACCENTS = {
|
|
363
391
|
r"\dot",
|
|
@@ -497,6 +525,22 @@ MATH_FUNCTIONS = {
|
|
|
497
525
|
r"\ext",
|
|
498
526
|
r"\pic",
|
|
499
527
|
r"\cl",
|
|
528
|
+
r"\jac",
|
|
529
|
+
r"\hes",
|
|
530
|
+
r"\wr",
|
|
531
|
+
r"\vol",
|
|
532
|
+
r"\rms",
|
|
533
|
+
r"\fft",
|
|
534
|
+
r"\dft",
|
|
535
|
+
r"\ord",
|
|
536
|
+
r"\val",
|
|
537
|
+
r"\num",
|
|
538
|
+
r"\den",
|
|
539
|
+
r"\sn",
|
|
540
|
+
r"\cn",
|
|
541
|
+
r"\dn",
|
|
542
|
+
r"\avg",
|
|
543
|
+
r"\len",
|
|
500
544
|
}
|
|
501
545
|
|
|
502
546
|
|
|
@@ -540,8 +584,10 @@ class ColorMathOptions:
|
|
|
540
584
|
color_differentials: bool = False
|
|
541
585
|
color_braket: bool = False
|
|
542
586
|
color_dimensionless: bool = False
|
|
543
|
-
color_alignment: bool =
|
|
544
|
-
color_single_constants: bool =
|
|
587
|
+
color_alignment: bool = False
|
|
588
|
+
color_single_constants: bool = False
|
|
589
|
+
normalize_braces: bool = False
|
|
590
|
+
extended_functions: bool = True
|
|
545
591
|
|
|
546
592
|
@classmethod
|
|
547
593
|
def extended(cls) -> ColorMathOptions:
|
|
@@ -556,6 +602,8 @@ class ColorMathOptions:
|
|
|
556
602
|
color_dimensionless=True,
|
|
557
603
|
color_alignment=True,
|
|
558
604
|
color_single_constants=True,
|
|
605
|
+
normalize_braces=False,
|
|
606
|
+
extended_functions=True,
|
|
559
607
|
)
|
|
560
608
|
|
|
561
609
|
@classmethod
|
|
@@ -569,6 +617,10 @@ class ColorMathOptions:
|
|
|
569
617
|
color_differentials=True,
|
|
570
618
|
color_braket=True,
|
|
571
619
|
color_dimensionless=True,
|
|
620
|
+
color_alignment=True,
|
|
621
|
+
color_single_constants=True,
|
|
622
|
+
normalize_braces=False,
|
|
623
|
+
extended_functions=True,
|
|
572
624
|
)
|
|
573
625
|
|
|
574
626
|
|
|
@@ -130,18 +130,28 @@ def convert_text(
|
|
|
130
130
|
|
|
131
131
|
Handles:
|
|
132
132
|
- single-line and multiline $$ ... $$ math blocks
|
|
133
|
+
- inline $ ... $ math expressions
|
|
133
134
|
- normal text passthrough
|
|
134
135
|
"""
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
scan = scan_markdown(text)
|
|
137
|
+
all_spans = sorted(
|
|
138
|
+
[*scan.math_blocks, *scan.math_inlines],
|
|
139
|
+
key=lambda s: s.start,
|
|
140
|
+
)
|
|
141
|
+
if not all_spans:
|
|
138
142
|
return text
|
|
139
143
|
|
|
140
144
|
converted: list[str] = []
|
|
141
145
|
index = 0
|
|
142
|
-
for span in
|
|
146
|
+
for span in all_spans:
|
|
143
147
|
converted.append(text[index:span.start])
|
|
144
|
-
|
|
148
|
+
if span.kind == "math_inline":
|
|
149
|
+
raw = text[span.content_start:span.content_end]
|
|
150
|
+
converted.append(f"${color_latex_body(raw, palette, options)}$")
|
|
151
|
+
else:
|
|
152
|
+
converted.append(
|
|
153
|
+
convert_math_block(text[span.start:span.end], palette, options)
|
|
154
|
+
)
|
|
145
155
|
index = span.end
|
|
146
156
|
converted.append(text[index:])
|
|
147
157
|
return "".join(converted)
|
|
@@ -15,6 +15,7 @@ from ..parsers.latex_spans import (
|
|
|
15
15
|
from ..parsers.scanner import collect_operator_spans
|
|
16
16
|
from ..utils.latex_helpers import contains_color_wrapper
|
|
17
17
|
from ..utils.spans import ColorSpan, apply_color_spans
|
|
18
|
+
from ..undo import uncolor_text
|
|
18
19
|
from .semantic import first_equality, parse_math_block, relation_spans, trim_range
|
|
19
20
|
|
|
20
21
|
|
|
@@ -221,7 +222,8 @@ def _rhs_spans(body: str, start: int, end: int | None = None) -> list[ColorSpan]
|
|
|
221
222
|
|
|
222
223
|
def convert_derivative_line(source: str) -> str | None:
|
|
223
224
|
"""Color a derivative block by inserting wrappers into its exact source."""
|
|
224
|
-
|
|
225
|
+
clean_source = uncolor_text(source) if contains_color_wrapper(source) else source
|
|
226
|
+
block = parse_math_block(clean_source)
|
|
225
227
|
if block is None:
|
|
226
228
|
return None
|
|
227
229
|
|
|
@@ -229,8 +231,6 @@ def convert_derivative_line(source: str) -> str | None:
|
|
|
229
231
|
prefix = read_operand(block.body, body_start)
|
|
230
232
|
if prefix is None or not _is_derivative_prefix(prefix.text(block.body)):
|
|
231
233
|
return None
|
|
232
|
-
if contains_color_wrapper(block.body):
|
|
233
|
-
return source
|
|
234
234
|
|
|
235
235
|
equality = first_equality(block.body)
|
|
236
236
|
if equality is None or equality[0] <= prefix.end:
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
6
|
|
|
7
|
-
from ..config import COLORS, ColorMathOptions
|
|
7
|
+
from ..config import COLORS, ColorMathOptions, get_bare_functions, BARE_FUNCTIONS
|
|
8
8
|
from ..parsers.constants import collect_single_constant_spans
|
|
9
9
|
from ..parsers.math_parser import find_semantic_spans
|
|
10
10
|
from ..parsers.scanner import collect_scanner_spans
|
|
@@ -17,6 +17,7 @@ from ..parsers.taxonomy import collect_taxonomy_spans
|
|
|
17
17
|
from ..parsers.variable_hash import collect_variable_spans
|
|
18
18
|
from ..utils.latex_helpers import contains_color_wrapper, normalize_latex_braces
|
|
19
19
|
from ..utils.spans import ColorSpan, apply_color_spans
|
|
20
|
+
from ..undo import uncolor_fragment
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
MATH_LINE_RE = re.compile(
|
|
@@ -25,10 +26,14 @@ MATH_LINE_RE = re.compile(
|
|
|
25
26
|
FUNCTION_COLOR_NAMES = ("main", "derivative", "chain")
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def collect_function_spans(
|
|
29
|
+
def collect_function_spans(
|
|
30
|
+
body: str,
|
|
31
|
+
palette: dict[str, str] | None = None,
|
|
32
|
+
bare_functions: frozenset[str] | None = None,
|
|
33
|
+
) -> list[ColorSpan]:
|
|
29
34
|
"""Color nested call names and recognize literal constants."""
|
|
30
35
|
pal = palette or COLORS
|
|
31
|
-
semantic, _ = find_semantic_spans(body)
|
|
36
|
+
semantic, _ = find_semantic_spans(body, bare_functions or BARE_FUNCTIONS)
|
|
32
37
|
spans: list[ColorSpan] = []
|
|
33
38
|
for item in semantic:
|
|
34
39
|
if item.kind == "function":
|
|
@@ -49,21 +54,38 @@ def color_latex_body(
|
|
|
49
54
|
options: ColorMathOptions | None = None,
|
|
50
55
|
) -> str:
|
|
51
56
|
"""Insert scoped colors while preserving every original source character."""
|
|
52
|
-
if contains_color_wrapper(body)
|
|
53
|
-
return body
|
|
57
|
+
clean_body = uncolor_fragment(body) if contains_color_wrapper(body) else body
|
|
54
58
|
|
|
55
59
|
pal = palette or COLORS
|
|
56
60
|
opts = options or ColorMathOptions()
|
|
57
61
|
|
|
58
|
-
normalized = normalize_latex_braces(
|
|
62
|
+
normalized = normalize_latex_braces(clean_body) if opts.normalize_braces else clean_body
|
|
63
|
+
|
|
64
|
+
need_units = opts.color_units or opts.enable_taxonomy or opts.variable_data_flow
|
|
65
|
+
need_diffs = opts.color_differentials or opts.enable_taxonomy or opts.variable_data_flow
|
|
66
|
+
need_dims = opts.color_dimensionless or opts.enable_taxonomy or opts.variable_data_flow
|
|
67
|
+
|
|
68
|
+
unit_spans = find_unit_spans(normalized) if need_units else []
|
|
69
|
+
diff_spans = find_differential_spans(normalized) if need_diffs else []
|
|
70
|
+
dim_spans = find_dimensionless_spans(normalized) if need_dims else []
|
|
71
|
+
|
|
72
|
+
bare_functions = (
|
|
73
|
+
get_bare_functions(opts)
|
|
74
|
+
if (opts.enable_taxonomy or opts.variable_data_flow)
|
|
75
|
+
else frozenset()
|
|
76
|
+
)
|
|
59
77
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
78
|
+
scanner_spans = collect_scanner_spans(normalized)
|
|
79
|
+
protected = [*unit_spans, *diff_spans, *dim_spans]
|
|
80
|
+
if protected:
|
|
81
|
+
scanner_spans = [
|
|
82
|
+
s for s in scanner_spans
|
|
83
|
+
if not any(p.start <= s.start and s.end <= p.end for p in protected)
|
|
84
|
+
]
|
|
63
85
|
|
|
64
86
|
spans: list[ColorSpan] = [
|
|
65
|
-
*collect_function_spans(normalized, pal),
|
|
66
|
-
*
|
|
87
|
+
*collect_function_spans(normalized, pal, bare_functions),
|
|
88
|
+
*scanner_spans,
|
|
67
89
|
]
|
|
68
90
|
|
|
69
91
|
if opts.color_units:
|
|
@@ -88,7 +110,7 @@ def color_latex_body(
|
|
|
88
110
|
spans.extend(collect_taxonomy_spans(normalized, pal, unit_spans, diff_spans, dim_spans))
|
|
89
111
|
|
|
90
112
|
if opts.variable_data_flow:
|
|
91
|
-
spans.extend(collect_variable_spans(normalized, None, unit_spans, diff_spans, dim_spans))
|
|
113
|
+
spans.extend(collect_variable_spans(normalized, None, unit_spans, diff_spans, dim_spans, bare_functions))
|
|
92
114
|
|
|
93
115
|
return apply_color_spans(normalized, spans)
|
|
94
116
|
|
|
@@ -9,6 +9,7 @@ from ..parsers.latex_spans import OperandSpan, find_operand_spans, read_operand
|
|
|
9
9
|
from ..parsers.scanner import collect_operator_spans, collect_structured_spans
|
|
10
10
|
from ..utils.latex_helpers import contains_color_wrapper
|
|
11
11
|
from ..utils.spans import ColorSpan, apply_color_spans
|
|
12
|
+
from ..undo import uncolor_text
|
|
12
13
|
from .semantic import first_equality, parse_math_block, relation_spans
|
|
13
14
|
|
|
14
15
|
|
|
@@ -79,6 +80,8 @@ def _operand_color_spans(
|
|
|
79
80
|
spans: list[ColorSpan] = []
|
|
80
81
|
semantic_index = 0
|
|
81
82
|
for operand in operands:
|
|
83
|
+
if operand.kind in ("matrix", "environment"):
|
|
84
|
+
continue
|
|
82
85
|
value = re.sub(r"\s+", "", operand.text(body))
|
|
83
86
|
if NUMBER_RE.fullmatch(value):
|
|
84
87
|
name = "orange"
|
|
@@ -102,11 +105,10 @@ def _operator_spans(body: str) -> list[ColorSpan]:
|
|
|
102
105
|
|
|
103
106
|
def convert_matrix_block(source: str) -> str | None:
|
|
104
107
|
"""Color complete matrix/tensor operands without reconstructing LaTeX."""
|
|
105
|
-
|
|
108
|
+
clean_source = uncolor_text(source) if contains_color_wrapper(source) else source
|
|
109
|
+
block = parse_math_block(clean_source)
|
|
106
110
|
if block is None or not _is_matrix_expression(block.body):
|
|
107
111
|
return None
|
|
108
|
-
if contains_color_wrapper(block.body):
|
|
109
|
-
return source
|
|
110
112
|
|
|
111
113
|
equality = first_equality(block.body)
|
|
112
114
|
if equality is None:
|
|
@@ -32,7 +32,7 @@ from .parsers.math_parser import describe_math_blocks
|
|
|
32
32
|
from .self_test import run_self_test
|
|
33
33
|
from .tutorial import run_tutorial
|
|
34
34
|
|
|
35
|
-
VERSION = "0.2.
|
|
35
|
+
VERSION = "0.2.1"
|
|
36
36
|
SUPPORTED_EXTENSIONS = {".md", ".markdown", ".qmd", ".ipynb", ".tex", ".latex"}
|
|
37
37
|
DEFAULT_EXCLUDES = {
|
|
38
38
|
".git",
|
|
@@ -297,6 +297,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
297
297
|
action="store_true",
|
|
298
298
|
help="Open the interactive graphical window with preview, colors, and settings.",
|
|
299
299
|
)
|
|
300
|
+
interactive_group.add_argument(
|
|
301
|
+
"--mcp",
|
|
302
|
+
action="store_true",
|
|
303
|
+
help="Launch the Model Context Protocol (MCP) server over stdio for AI assistants.",
|
|
304
|
+
)
|
|
300
305
|
interactive_group.add_argument(
|
|
301
306
|
"--tutorial",
|
|
302
307
|
action="store_true",
|
|
@@ -584,6 +589,16 @@ def _main_impl(argv: list[str] | None = None) -> int:
|
|
|
584
589
|
options=options,
|
|
585
590
|
)
|
|
586
591
|
|
|
592
|
+
# Launch Model Context Protocol (MCP) Server
|
|
593
|
+
if args.mcp:
|
|
594
|
+
try:
|
|
595
|
+
from .mcp_server import run_mcp_server
|
|
596
|
+
except ImportError as exc:
|
|
597
|
+
sys.stderr.write(f"Error starting Color Math MCP server:\n{exc}\n")
|
|
598
|
+
return 1
|
|
599
|
+
run_mcp_server(transport="stdio")
|
|
600
|
+
return 0
|
|
601
|
+
|
|
587
602
|
# Generate shell autocompletion script
|
|
588
603
|
if args.generate_completion:
|
|
589
604
|
script = get_completion_script(args.generate_completion)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Direct module entrypoint for running the Color Math MCP server: python -m color_math.mcp"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> None:
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
prog="color-math-mcp",
|
|
12
|
+
description="Run the Python Color Math Model Context Protocol (MCP) server.",
|
|
13
|
+
)
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--transport",
|
|
16
|
+
choices=["stdio", "sse", "streamable-http"],
|
|
17
|
+
default="stdio",
|
|
18
|
+
help="Transport protocol (default: stdio)",
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--port",
|
|
22
|
+
type=int,
|
|
23
|
+
default=8000,
|
|
24
|
+
help="Port for SSE or HTTP transport (default: 8000)",
|
|
25
|
+
)
|
|
26
|
+
args = parser.parse_args()
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
from .mcp_server import run_mcp_server
|
|
30
|
+
except ImportError as e:
|
|
31
|
+
print(f"Error starting Color Math MCP server:\n{e}", file=sys.stderr)
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
extra_kwargs = {}
|
|
35
|
+
if args.transport in {"sse", "streamable-http"}:
|
|
36
|
+
extra_kwargs["port"] = args.port
|
|
37
|
+
|
|
38
|
+
run_mcp_server(transport=args.transport, **extra_kwargs)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
main()
|