This is page 29 of 35. Use http://codebase.md/pragmar/mcp_server_webcrawl?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .gitignore
├── CONTRIBUTING.md
├── docs
│ ├── _images
│ │ ├── interactive.document.webp
│ │ ├── interactive.search.webp
│ │ └── mcpswc.svg
│ ├── _modules
│ │ ├── index.html
│ │ ├── mcp_server_webcrawl
│ │ │ ├── crawlers
│ │ │ │ ├── archivebox
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ └── tests.html
│ │ │ │ ├── base
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── api.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ ├── indexed.html
│ │ │ │ │ └── tests.html
│ │ │ │ ├── httrack
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ └── tests.html
│ │ │ │ ├── interrobot
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ └── tests.html
│ │ │ │ ├── katana
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ └── tests.html
│ │ │ │ ├── siteone
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ └── tests.html
│ │ │ │ ├── warc
│ │ │ │ │ ├── adapter.html
│ │ │ │ │ ├── crawler.html
│ │ │ │ │ └── tests.html
│ │ │ │ └── wget
│ │ │ │ ├── adapter.html
│ │ │ │ ├── crawler.html
│ │ │ │ └── tests.html
│ │ │ ├── crawlers.html
│ │ │ ├── extras
│ │ │ │ ├── markdown.html
│ │ │ │ ├── regex.html
│ │ │ │ ├── snippets.html
│ │ │ │ ├── thumbnails.html
│ │ │ │ └── xpath.html
│ │ │ ├── interactive
│ │ │ │ ├── highlights.html
│ │ │ │ ├── search.html
│ │ │ │ ├── session.html
│ │ │ │ └── ui.html
│ │ │ ├── main.html
│ │ │ ├── models
│ │ │ │ ├── resources.html
│ │ │ │ └── sites.html
│ │ │ ├── templates
│ │ │ │ └── tests.html
│ │ │ ├── utils
│ │ │ │ ├── blobs.html
│ │ │ │ ├── cli.html
│ │ │ │ ├── logger.html
│ │ │ │ ├── querycache.html
│ │ │ │ ├── server.html
│ │ │ │ └── tools.html
│ │ │ └── utils.html
│ │ └── re.html
│ ├── _sources
│ │ ├── guides
│ │ │ ├── archivebox.rst.txt
│ │ │ ├── httrack.rst.txt
│ │ │ ├── interrobot.rst.txt
│ │ │ ├── katana.rst.txt
│ │ │ ├── siteone.rst.txt
│ │ │ ├── warc.rst.txt
│ │ │ └── wget.rst.txt
│ │ ├── guides.rst.txt
│ │ ├── index.rst.txt
│ │ ├── installation.rst.txt
│ │ ├── interactive.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.archivebox.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.base.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.httrack.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.interrobot.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.katana.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.siteone.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.warc.rst.txt
│ │ ├── mcp_server_webcrawl.crawlers.wget.rst.txt
│ │ ├── mcp_server_webcrawl.extras.rst.txt
│ │ ├── mcp_server_webcrawl.interactive.rst.txt
│ │ ├── mcp_server_webcrawl.models.rst.txt
│ │ ├── mcp_server_webcrawl.rst.txt
│ │ ├── mcp_server_webcrawl.templates.rst.txt
│ │ ├── mcp_server_webcrawl.utils.rst.txt
│ │ ├── modules.rst.txt
│ │ ├── prompts.rst.txt
│ │ └── usage.rst.txt
│ ├── _static
│ │ ├── _sphinx_javascript_frameworks_compat.js
│ │ ├── basic.css
│ │ ├── css
│ │ │ ├── badge_only.css
│ │ │ ├── fonts
│ │ │ │ ├── fontawesome-webfont.eot
│ │ │ │ ├── fontawesome-webfont.svg
│ │ │ │ ├── fontawesome-webfont.ttf
│ │ │ │ ├── fontawesome-webfont.woff
│ │ │ │ ├── fontawesome-webfont.woff2
│ │ │ │ ├── lato-bold-italic.woff
│ │ │ │ ├── lato-bold-italic.woff2
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-normal-italic.woff
│ │ │ │ ├── lato-normal-italic.woff2
│ │ │ │ ├── lato-normal.woff
│ │ │ │ ├── lato-normal.woff2
│ │ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ │ └── Roboto-Slab-Regular.woff2
│ │ │ └── theme.css
│ │ ├── doctools.js
│ │ ├── documentation_options.js
│ │ ├── file.png
│ │ ├── fonts
│ │ │ ├── Lato
│ │ │ │ ├── lato-bold.eot
│ │ │ │ ├── lato-bold.ttf
│ │ │ │ ├── lato-bold.woff
│ │ │ │ ├── lato-bold.woff2
│ │ │ │ ├── lato-bolditalic.eot
│ │ │ │ ├── lato-bolditalic.ttf
│ │ │ │ ├── lato-bolditalic.woff
│ │ │ │ ├── lato-bolditalic.woff2
│ │ │ │ ├── lato-italic.eot
│ │ │ │ ├── lato-italic.ttf
│ │ │ │ ├── lato-italic.woff
│ │ │ │ ├── lato-italic.woff2
│ │ │ │ ├── lato-regular.eot
│ │ │ │ ├── lato-regular.ttf
│ │ │ │ ├── lato-regular.woff
│ │ │ │ └── lato-regular.woff2
│ │ │ └── RobotoSlab
│ │ │ ├── roboto-slab-v7-bold.eot
│ │ │ ├── roboto-slab-v7-bold.ttf
│ │ │ ├── roboto-slab-v7-bold.woff
│ │ │ ├── roboto-slab-v7-bold.woff2
│ │ │ ├── roboto-slab-v7-regular.eot
│ │ │ ├── roboto-slab-v7-regular.ttf
│ │ │ ├── roboto-slab-v7-regular.woff
│ │ │ └── roboto-slab-v7-regular.woff2
│ │ ├── images
│ │ │ ├── interactive.document.png
│ │ │ ├── interactive.document.webp
│ │ │ ├── interactive.search.png
│ │ │ ├── interactive.search.webp
│ │ │ └── mcpswc.svg
│ │ ├── jquery.js
│ │ ├── js
│ │ │ ├── badge_only.js
│ │ │ ├── theme.js
│ │ │ └── versions.js
│ │ ├── language_data.js
│ │ ├── minus.png
│ │ ├── plus.png
│ │ ├── pygments.css
│ │ ├── searchtools.js
│ │ └── sphinx_highlight.js
│ ├── .buildinfo
│ ├── .nojekyll
│ ├── genindex.html
│ ├── guides
│ │ ├── archivebox.html
│ │ ├── httrack.html
│ │ ├── interrobot.html
│ │ ├── katana.html
│ │ ├── siteone.html
│ │ ├── warc.html
│ │ └── wget.html
│ ├── guides.html
│ ├── index.html
│ ├── installation.html
│ ├── interactive.html
│ ├── mcp_server_webcrawl.crawlers.archivebox.html
│ ├── mcp_server_webcrawl.crawlers.base.html
│ ├── mcp_server_webcrawl.crawlers.html
│ ├── mcp_server_webcrawl.crawlers.httrack.html
│ ├── mcp_server_webcrawl.crawlers.interrobot.html
│ ├── mcp_server_webcrawl.crawlers.katana.html
│ ├── mcp_server_webcrawl.crawlers.siteone.html
│ ├── mcp_server_webcrawl.crawlers.warc.html
│ ├── mcp_server_webcrawl.crawlers.wget.html
│ ├── mcp_server_webcrawl.extras.html
│ ├── mcp_server_webcrawl.html
│ ├── mcp_server_webcrawl.interactive.html
│ ├── mcp_server_webcrawl.models.html
│ ├── mcp_server_webcrawl.templates.html
│ ├── mcp_server_webcrawl.utils.html
│ ├── modules.html
│ ├── objects.inv
│ ├── prompts.html
│ ├── py-modindex.html
│ ├── search.html
│ ├── searchindex.js
│ └── usage.html
├── LICENSE
├── MANIFEST.in
├── prompts
│ ├── audit404.md
│ ├── auditfiles.md
│ ├── auditperf.md
│ ├── auditseo.md
│ ├── gopher.md
│ ├── README.md
│ └── testsearch.md
├── pyproject.toml
├── README.md
├── setup.py
├── sphinx
│ ├── _static
│ │ └── images
│ │ ├── interactive.document.png
│ │ ├── interactive.document.webp
│ │ ├── interactive.search.png
│ │ ├── interactive.search.webp
│ │ └── mcpswc.svg
│ ├── _templates
│ │ └── layout.html
│ ├── conf.py
│ ├── guides
│ │ ├── archivebox.rst
│ │ ├── httrack.rst
│ │ ├── interrobot.rst
│ │ ├── katana.rst
│ │ ├── siteone.rst
│ │ ├── warc.rst
│ │ └── wget.rst
│ ├── guides.rst
│ ├── index.rst
│ ├── installation.rst
│ ├── interactive.rst
│ ├── make.bat
│ ├── Makefile
│ ├── mcp_server_webcrawl.crawlers.archivebox.rst
│ ├── mcp_server_webcrawl.crawlers.base.rst
│ ├── mcp_server_webcrawl.crawlers.httrack.rst
│ ├── mcp_server_webcrawl.crawlers.interrobot.rst
│ ├── mcp_server_webcrawl.crawlers.katana.rst
│ ├── mcp_server_webcrawl.crawlers.rst
│ ├── mcp_server_webcrawl.crawlers.siteone.rst
│ ├── mcp_server_webcrawl.crawlers.warc.rst
│ ├── mcp_server_webcrawl.crawlers.wget.rst
│ ├── mcp_server_webcrawl.extras.rst
│ ├── mcp_server_webcrawl.interactive.rst
│ ├── mcp_server_webcrawl.models.rst
│ ├── mcp_server_webcrawl.rst
│ ├── mcp_server_webcrawl.templates.rst
│ ├── mcp_server_webcrawl.utils.rst
│ ├── modules.rst
│ ├── prompts.rst
│ ├── readme.txt
│ └── usage.rst
└── src
└── mcp_server_webcrawl
├── __init__.py
├── crawlers
│ ├── __init__.py
│ ├── archivebox
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── crawler.py
│ │ └── tests.py
│ ├── base
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── api.py
│ │ ├── crawler.py
│ │ ├── indexed.py
│ │ └── tests.py
│ ├── httrack
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── crawler.py
│ │ └── tests.py
│ ├── interrobot
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── crawler.py
│ │ └── tests.py
│ ├── katana
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── crawler.py
│ │ └── tests.py
│ ├── siteone
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── crawler.py
│ │ └── tests.py
│ ├── warc
│ │ ├── __init__.py
│ │ ├── adapter.py
│ │ ├── crawler.py
│ │ └── tests.py
│ └── wget
│ ├── __init__.py
│ ├── adapter.py
│ ├── crawler.py
│ └── tests.py
├── extras
│ ├── __init__.py
│ ├── markdown.py
│ ├── regex.py
│ ├── snippets.py
│ ├── thumbnails.py
│ └── xpath.py
├── interactive
│ ├── __init__.py
│ ├── highlights.py
│ ├── search.py
│ ├── session.py
│ ├── ui.py
│ └── views
│ ├── base.py
│ ├── document.py
│ ├── help.py
│ ├── requirements.py
│ ├── results.py
│ └── searchform.py
├── main.py
├── models
│ ├── __init__.py
│ ├── base.py
│ ├── resources.py
│ └── sites.py
├── settings.py
├── templates
│ ├── __init__.py
│ ├── markdown.xslt
│ ├── tests_core.html
│ └── tests.py
└── utils
├── __init__.py
├── cli.py
├── logger.py
├── parser.py
├── parsetab.py
├── search.py
├── server.py
├── tests.py
└── tools.py
```
# Files
--------------------------------------------------------------------------------
/docs/_modules/mcp_server_webcrawl/interactive/ui.html:
--------------------------------------------------------------------------------
```html
1 |
2 |
3 | <!DOCTYPE html>
4 | <html class="writer-html5" lang="en" data-content_root="../../../">
5 | <head>
6 | <meta charset="utf-8" />
7 | <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8 | <title>mcp_server_webcrawl.interactive.ui — mcp-server-webcrawl documentation</title>
9 | <link rel="stylesheet" type="text/css" href="../../../_static/pygments.css?v=80d5e7a1" />
10 | <link rel="stylesheet" type="text/css" href="../../../_static/css/theme.css?v=e59714d7" />
11 |
12 |
13 | <script src="../../../_static/jquery.js?v=5d32c60e"></script>
14 | <script src="../../../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
15 | <script src="../../../_static/documentation_options.js?v=5929fcd5"></script>
16 | <script src="../../../_static/doctools.js?v=888ff710"></script>
17 | <script src="../../../_static/sphinx_highlight.js?v=dc90522c"></script>
18 | <script src="../../../_static/js/theme.js"></script>
19 | <link rel="index" title="Index" href="../../../genindex.html" />
20 | <link rel="search" title="Search" href="../../../search.html" />
21 | </head>
22 |
23 | <body class="wy-body-for-nav">
24 | <div class="wy-grid-for-nav">
25 | <nav data-toggle="wy-nav-shift" class="wy-nav-side">
26 | <div class="wy-side-scroll">
27 | <div class="wy-side-nav-search" >
28 |
29 |
30 |
31 | <a href="../../../index.html" class="icon icon-home">
32 | mcp-server-webcrawl
33 | </a>
34 | <div role="search">
35 | <form id="rtd-search-form" class="wy-form" action="../../../search.html" method="get">
36 | <input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
37 | <input type="hidden" name="check_keywords" value="yes" />
38 | <input type="hidden" name="area" value="default" />
39 | </form>
40 | </div>
41 | </div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
42 | <p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
43 | <ul>
44 | <li class="toctree-l1"><a class="reference internal" href="../../../installation.html">Installation</a></li>
45 | <li class="toctree-l1"><a class="reference internal" href="../../../guides.html">Setup Guides</a></li>
46 | <li class="toctree-l1"><a class="reference internal" href="../../../usage.html">Usage</a></li>
47 | <li class="toctree-l1"><a class="reference internal" href="../../../prompts.html">Prompt Routines</a></li>
48 | <li class="toctree-l1"><a class="reference internal" href="../../../interactive.html">Interactive Mode</a></li>
49 | <li class="toctree-l1"><a class="reference internal" href="../../../modules.html">mcp_server_webcrawl</a></li>
50 | </ul>
51 |
52 | </div>
53 | </div>
54 | </nav>
55 |
56 | <section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
57 | <i data-toggle="wy-nav-top" class="fa fa-bars"></i>
58 | <a href="../../../index.html">mcp-server-webcrawl</a>
59 | </nav>
60 |
61 | <div class="wy-nav-content">
62 | <div class="rst-content">
63 | <div role="navigation" aria-label="Page navigation">
64 | <ul class="wy-breadcrumbs">
65 | <li><a href="../../../index.html" class="icon icon-home" aria-label="Home"></a></li>
66 | <li class="breadcrumb-item"><a href="../../index.html">Module code</a></li>
67 | <li class="breadcrumb-item active">mcp_server_webcrawl.interactive.ui</li>
68 | <li class="wy-breadcrumbs-aside">
69 | </li>
70 | </ul>
71 | <hr/>
72 | </div>
73 | <div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
74 | <div itemprop="articleBody">
75 |
76 | <h1>Source code for mcp_server_webcrawl.interactive.ui</h1><div class="highlight"><pre>
77 | <span></span><span class="kn">import</span> <span class="nn">curses</span>
78 |
79 | <span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">Enum</span><span class="p">,</span> <span class="n">auto</span>
80 | <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">NamedTuple</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span>
81 |
82 | <span class="kn">from</span> <span class="nn">mcp_server_webcrawl.crawlers</span> <span class="kn">import</span> <span class="n">VALID_CRAWLER_CHOICES</span>
83 |
84 | <span class="n">SITE_COLUMN_WIDTH</span> <span class="o">=</span> <span class="mi">18</span>
85 |
86 | <span class="n">LAYOUT_GRID_COLUMN_SPACING</span> <span class="o">=</span> <span class="mi">2</span>
87 | <span class="n">LAYOUT_CONSTRAINED_SITES_PER_COLUMN</span> <span class="o">=</span> <span class="mi">3</span>
88 | <span class="n">LAYOUT_SITES_GRID_OFFSET</span> <span class="o">=</span> <span class="mi">6</span>
89 |
90 | <span class="n">DEFAULT_GROUP_WIDTH</span> <span class="o">=</span> <span class="mi">12</span>
91 |
92 | <span class="n">INPUT_BOX_BRACKET_WIDTH</span> <span class="o">=</span> <span class="mi">2</span>
93 | <span class="n">CURSOR_SCROLL_THRESHOLD</span> <span class="o">=</span> <span class="mi">5</span>
94 |
95 | <div class="viewcode-block" id="DocumentMode">
96 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.DocumentMode">[docs]</a>
97 | <span class="k">class</span> <span class="nc">DocumentMode</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
98 | <span class="n">MARKDOWN</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
99 | <span class="n">RAW</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
100 | <span class="n">HEADERS</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span></div>
101 |
102 |
103 | <div class="viewcode-block" id="NavigationDirection">
104 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.NavigationDirection">[docs]</a>
105 | <span class="k">class</span> <span class="nc">NavigationDirection</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
106 | <span class="n">UP</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
107 | <span class="n">DOWN</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
108 | <span class="n">LEFT</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
109 | <span class="n">RIGHT</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span></div>
110 |
111 |
112 | <div class="viewcode-block" id="ScrollDirection">
113 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.ScrollDirection">[docs]</a>
114 | <span class="k">class</span> <span class="nc">ScrollDirection</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
115 | <span class="n">UP</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
116 | <span class="n">DOWN</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span></div>
117 |
118 |
119 | <div class="viewcode-block" id="SearchFilterType">
120 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.SearchFilterType">[docs]</a>
121 | <span class="k">class</span> <span class="nc">SearchFilterType</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
122 | <span class="n">ANY</span> <span class="o">=</span> <span class="mi">0</span>
123 | <span class="n">PAGES</span> <span class="o">=</span> <span class="mi">1</span></div>
124 |
125 |
126 | <div class="viewcode-block" id="ThemeDefinition">
127 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.ThemeDefinition">[docs]</a>
128 | <span class="k">class</span> <span class="nc">ThemeDefinition</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
129 | <span class="c1"># https://www.ditig.com/256-colors-cheat-sheet</span>
130 | <span class="n">DOCUMENT_MODE</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_BLUE</span><span class="p">,</span> <span class="mi">51</span><span class="p">)</span>
131 | <span class="n">HEADER_ACTIVE</span> <span class="o">=</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>
132 | <span class="n">HEADER_INACTIVE</span> <span class="o">=</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">233</span><span class="p">)</span>
133 | <span class="n">HEADER_OUTER</span> <span class="o">=</span> <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">235</span><span class="p">)</span>
134 | <span class="n">HELP_LINK</span> <span class="o">=</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">27</span><span class="p">)</span>
135 | <span class="n">HTTP_ERROR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">88</span><span class="p">)</span>
136 | <span class="n">HTTP_WARN</span> <span class="o">=</span> <span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">130</span><span class="p">)</span>
137 | <span class="n">INACTIVE_QUERY</span> <span class="o">=</span> <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">245</span><span class="p">,</span> <span class="mi">237</span><span class="p">)</span>
138 | <span class="n">SNIPPET_DEFAULT</span> <span class="o">=</span> <span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mi">243</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_DIM</span><span class="p">)</span>
139 | <span class="n">SNIPPET_HIGHLIGHT</span> <span class="o">=</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">232</span><span class="p">,</span> <span class="mi">51</span><span class="p">)</span>
140 | <span class="n">UI_ERROR</span> <span class="o">=</span> <span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">COLOR_WHITE</span><span class="p">,</span> <span class="mi">88</span><span class="p">)</span></div>
141 |
142 |
143 | <div class="viewcode-block" id="UiFocusable">
144 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.UiFocusable">[docs]</a>
145 | <span class="k">class</span> <span class="nc">UiFocusable</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
146 | <span class="n">UNDEFINED</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
147 | <span class="n">SEARCH_FORM</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
148 | <span class="n">SEARCH_RESULTS</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span></div>
149 |
150 |
151 | <div class="viewcode-block" id="UiState">
152 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.UiState">[docs]</a>
153 | <span class="k">class</span> <span class="nc">UiState</span><span class="p">(</span><span class="n">Enum</span><span class="p">):</span>
154 | <span class="n">UNDEFINED</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
155 | <span class="n">REQUIREMENTS</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
156 | <span class="n">SEARCH_INIT</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
157 | <span class="n">SEARCH_RESULTS</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
158 | <span class="n">DOCUMENT</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span>
159 | <span class="n">HELP</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span></div>
160 |
161 |
162 | <div class="viewcode-block" id="safe_addstr">
163 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.safe_addstr">[docs]</a>
164 | <span class="k">def</span> <span class="nf">safe_addstr</span><span class="p">(</span><span class="n">stdscr</span><span class="p">:</span> <span class="n">curses</span><span class="o">.</span><span class="n">window</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">text</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">style</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_NORMAL</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
165 | <span class="w"> </span><span class="sd">"""</span>
166 | <span class="sd"> Safe addstr that handles screen edge errors.</span>
167 | <span class="sd"> """</span>
168 | <span class="k">try</span><span class="p">:</span>
169 | <span class="n">stdscr</span><span class="o">.</span><span class="n">addstr</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">text</span><span class="p">,</span> <span class="n">style</span><span class="p">)</span>
170 | <span class="k">except</span> <span class="n">curses</span><span class="o">.</span><span class="n">error</span><span class="p">:</span>
171 | <span class="k">pass</span></div>
172 |
173 |
174 | <div class="viewcode-block" id="InputRadio">
175 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadio">[docs]</a>
176 | <span class="k">class</span> <span class="nc">InputRadio</span><span class="p">:</span>
177 | <div class="viewcode-block" id="InputRadio.__init__">
178 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadio.__init__">[docs]</a>
179 | <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">group</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">label</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">index</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">states</span><span class="p">:</span> <span class="nb">list</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
180 | <span class="w"> </span><span class="sd">"""</span>
181 | <span class="sd"> Radio input with 2-3 possible states (e.g., on/off or state1/state2/off)</span>
182 | <span class="sd"> </span>
183 | <span class="sd"> Args:</span>
184 | <span class="sd"> group: The InputRadioGroup this radio belongs to</span>
185 | <span class="sd"> name: The form radio group name</span>
186 | <span class="sd"> label: The form radio label </span>
187 | <span class="sd"> index: The current state index</span>
188 | <span class="sd"> states: List of InputRadioState objects defining each possible state </span>
189 | <span class="sd"> """</span>
190 | <span class="c1"># used like so states_radio = InputRadio(groupname, label, index, states=InputRadioGroup.get_filters())</span>
191 | <span class="k">if</span> <span class="n">states</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
192 | <span class="n">states</span> <span class="o">=</span> <span class="p">[]</span>
193 | <span class="k">assert</span> <span class="n">states</span><span class="p">,</span> <span class="s2">"states must be provided and non-empty"</span>
194 | <span class="k">assert</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">index</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">states</span><span class="p">),</span> <span class="sa">f</span><span class="s2">"index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s2"> out of range for </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">states</span><span class="p">)</span><span class="si">}</span><span class="s2"> states"</span>
195 |
196 | <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span>
197 | <span class="bp">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span>
198 | <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">index</span>
199 | <span class="bp">self</span><span class="o">.</span><span class="n">_states</span> <span class="o">=</span> <span class="n">states</span>
200 | <span class="bp">self</span><span class="o">.</span><span class="n">_group</span> <span class="o">=</span> <span class="n">group</span></div>
201 |
202 |
203 |
204 | <span class="nd">@property</span>
205 | <span class="k">def</span> <span class="nf">current_state</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
206 | <span class="w"> </span><span class="sd">"""</span>
207 | <span class="sd"> Get the current state</span>
208 | <span class="sd"> """</span>
209 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_states</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">index</span><span class="p">]</span>
210 |
211 | <span class="nd">@property</span>
212 | <span class="k">def</span> <span class="nf">display_label</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
213 | <span class="w"> </span><span class="sd">"""</span>
214 | <span class="sd"> Get the current display label</span>
215 | <span class="sd"> """</span>
216 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_state</span><span class="o">.</span><span class="n">label</span>
217 |
218 | <span class="nd">@property</span>
219 | <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
220 | <span class="w"> </span><span class="sd">"""</span>
221 | <span class="sd"> Get the current value</span>
222 | <span class="sd"> """</span>
223 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_state</span><span class="o">.</span><span class="n">value</span>
224 |
225 | <div class="viewcode-block" id="InputRadio.next_state">
226 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadio.next_state">[docs]</a>
227 | <span class="k">def</span> <span class="nf">next_state</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
228 | <span class="w"> </span><span class="sd">"""</span>
229 | <span class="sd"> Cycle to the next state</span>
230 | <span class="sd"> """</span>
231 | <span class="c1"># clear group for single-selection radios</span>
232 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_group</span><span class="o">.</span><span class="n">name</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"filter"</span><span class="p">,</span> <span class="s2">"site"</span><span class="p">,</span> <span class="s2">"crawler"</span><span class="p">]:</span>
233 | <span class="bp">self</span><span class="o">.</span><span class="n">_group</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
234 |
235 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_group</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"sort"</span><span class="p">:</span>
236 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span> <span class="c1"># inactive " " -> active ascending "+"</span>
237 | <span class="bp">self</span><span class="o">.</span><span class="n">_group</span><span class="o">.</span><span class="n">clear</span><span class="p">()</span>
238 | <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="mi">1</span>
239 | <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span> <span class="c1"># ascending "+" -> descending "-"</span>
240 | <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="mi">2</span>
241 | <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span> <span class="c1"># descending "-" -> ascending "+"</span>
242 | <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="mi">1</span>
243 | <span class="k">else</span><span class="p">:</span>
244 | <span class="c1"># standard cycling for other radios</span>
245 | <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_states</span><span class="p">)</span></div>
246 |
247 |
248 | <div class="viewcode-block" id="InputRadio.render">
249 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadio.render">[docs]</a>
250 | <span class="k">def</span> <span class="nf">render</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stdscr</span><span class="p">:</span> <span class="n">curses</span><span class="o">.</span><span class="n">window</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">field_index</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">max_width</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">focused</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
251 | <span class="w"> </span><span class="sd">"""</span>
252 | <span class="sd"> Render a single radio option.</span>
253 | <span class="sd"> """</span>
254 |
255 | <span class="n">radio_symbol</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">display_label</span>
256 | <span class="n">display_text</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">label</span>
257 | <span class="k">if</span> <span class="n">max_width</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">display_text</span><span class="p">)</span> <span class="o">></span> <span class="n">max_width</span><span class="p">:</span>
258 | <span class="n">display_text</span> <span class="o">=</span> <span class="n">display_text</span><span class="p">[:</span><span class="n">max_width</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="s2">"…"</span>
259 |
260 | <span class="n">line</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"(</span><span class="si">{</span><span class="n">radio_symbol</span><span class="si">}</span><span class="s2">) </span><span class="si">{</span><span class="n">display_text</span><span class="si">}</span><span class="s2">"</span>
261 | <span class="n">style</span> <span class="o">=</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_REVERSE</span> <span class="k">if</span> <span class="n">focused</span> <span class="k">else</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_NORMAL</span>
262 |
263 | <span class="k">try</span><span class="p">:</span>
264 | <span class="n">safe_addstr</span><span class="p">(</span><span class="n">stdscr</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">line</span><span class="p">,</span> <span class="n">style</span><span class="p">)</span>
265 | <span class="k">except</span> <span class="n">curses</span><span class="o">.</span><span class="n">error</span><span class="p">:</span>
266 | <span class="k">pass</span> <span class="c1"># screen edge</span></div>
267 |
268 |
269 | <div class="viewcode-block" id="InputRadio.set_state">
270 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadio.set_state">[docs]</a>
271 | <span class="k">def</span> <span class="nf">set_state</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">index</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
272 | <span class="w"> </span><span class="sd">"""</span>
273 | <span class="sd"> Set the current state by index</span>
274 | <span class="sd"> """</span>
275 | <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">index</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_states</span><span class="p">):</span>
276 | <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">index</span>
277 | <span class="k">else</span><span class="p">:</span>
278 | <span class="k">raise</span> <span class="ne">IndexError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"State index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s2"> out of range"</span><span class="p">)</span></div>
279 |
280 |
281 | <div class="viewcode-block" id="InputRadio.set_states">
282 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadio.set_states">[docs]</a>
283 | <span class="k">def</span> <span class="nf">set_states</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">states</span><span class="p">:</span> <span class="nb">list</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
284 | <span class="w"> </span><span class="sd">"""</span>
285 | <span class="sd"> Set the current state by index</span>
286 | <span class="sd"> """</span>
287 | <span class="bp">self</span><span class="o">.</span><span class="n">_states</span> <span class="o">=</span> <span class="n">states</span></div>
288 |
289 |
290 | <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
291 | <span class="k">return</span> <span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">label</span><span class="si">}</span><span class="s2">: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">display_label</span><span class="si">}</span><span class="s2"> (</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="si">}</span><span class="s2">)"</span></div>
292 |
293 |
294 | <div class="viewcode-block" id="InputRadioGroup">
295 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup">[docs]</a>
296 | <span class="k">class</span> <span class="nc">InputRadioGroup</span><span class="p">:</span>
297 | <span class="w"> </span><span class="sd">"""</span>
298 | <span class="sd"> Radio group with navigation and layout management capabilities.</span>
299 | <span class="sd"> """</span>
300 | <div class="viewcode-block" id="InputRadioGroup.__init__">
301 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.__init__">[docs]</a>
302 | <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">sites</span><span class="p">:</span> <span class="nb">list</span> <span class="o">=</span> <span class="kc">None</span><span class="p">):</span>
303 | <span class="w"> </span><span class="sd">"""</span>
304 | <span class="sd"> Radio input group with layout and navigation support.</span>
305 | <span class="sd"> </span>
306 | <span class="sd"> Args:</span>
307 | <span class="sd"> name: The form radio group name ("filter", "sort", "site", or "crawler")</span>
308 | <span class="sd"> sites: List of SiteResult objects, required only for "site" group type</span>
309 | <span class="sd"> """</span>
310 | <span class="n">sites</span> <span class="o">=</span> <span class="n">sites</span> <span class="k">if</span> <span class="n">sites</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="k">else</span> <span class="p">[]</span>
311 | <span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">name</span>
312 | <span class="bp">self</span><span class="o">.</span><span class="n">label</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">name</span>
313 | <span class="bp">self</span><span class="o">.</span><span class="n">__selected_index</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
314 |
315 | <span class="c1"># layout configuration</span>
316 | <span class="bp">self</span><span class="o">.</span><span class="n">__available_width</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
317 | <span class="bp">self</span><span class="o">.</span><span class="n">__available_height</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
318 | <span class="bp">self</span><span class="o">.</span><span class="n">__is_constrained</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span>
319 | <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
320 | <span class="bp">self</span><span class="o">.</span><span class="n">__max_columns</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span>
321 |
322 | <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">InputRadio</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
323 |
324 | <span class="n">group_config</span> <span class="o">=</span> <span class="p">{</span>
325 | <span class="s2">"filter"</span><span class="p">:</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__load_filters</span><span class="p">,</span> <span class="s2">"Filter:"</span><span class="p">),</span>
326 | <span class="s2">"site"</span><span class="p">:</span> <span class="p">(</span><span class="k">lambda</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">__load_sites</span><span class="p">(</span><span class="n">sites</span><span class="p">),</span> <span class="s2">"Sites:"</span><span class="p">),</span>
327 | <span class="s2">"sort"</span><span class="p">:</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__load_sorts</span><span class="p">,</span> <span class="s2">"Sorts:"</span><span class="p">),</span>
328 | <span class="s2">"crawler"</span><span class="p">:</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__load_crawlers</span><span class="p">,</span> <span class="s2">"Crawlers:"</span><span class="p">),</span>
329 | <span class="p">}</span>
330 |
331 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="ow">in</span> <span class="n">group_config</span><span class="p">:</span>
332 | <span class="n">data_loader</span><span class="p">,</span> <span class="n">label</span> <span class="o">=</span> <span class="n">group_config</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="p">]</span>
333 | <span class="bp">self</span><span class="o">.</span><span class="n">label</span> <span class="o">=</span> <span class="n">label</span>
334 | <span class="n">data_loader</span><span class="p">()</span>
335 | <span class="k">else</span><span class="p">:</span>
336 | <span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unsupported radio option: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
337 |
338 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">:</span>
339 | <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">next_state</span><span class="p">()</span></div>
340 |
341 |
342 | <span class="nd">@property</span>
343 | <span class="k">def</span> <span class="nf">value</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
344 | <span class="k">for</span> <span class="n">radio</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">:</span>
345 | <span class="k">if</span> <span class="n">radio</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="s2">"on"</span> <span class="ow">or</span> <span class="n">radio</span><span class="o">.</span><span class="n">display_label</span> <span class="ow">in</span> <span class="p">[</span><span class="s2">"+"</span><span class="p">,</span> <span class="s2">"-"</span><span class="p">]:</span> <span class="c1"># selected state</span>
346 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"filter"</span><span class="p">:</span>
347 | <span class="k">return</span> <span class="s2">"html"</span> <span class="k">if</span> <span class="n">radio</span><span class="o">.</span><span class="n">label</span> <span class="o">==</span> <span class="s2">"HTML"</span> <span class="k">else</span> <span class="s2">""</span>
348 | <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"sort"</span><span class="p">:</span>
349 | <span class="k">if</span> <span class="n">radio</span><span class="o">.</span><span class="n">display_label</span> <span class="o">==</span> <span class="s2">"+"</span><span class="p">:</span>
350 | <span class="k">return</span> <span class="sa">f</span><span class="s2">"+</span><span class="si">{</span><span class="n">radio</span><span class="o">.</span><span class="n">label</span><span class="si">}</span><span class="s2">"</span>
351 | <span class="k">elif</span> <span class="n">radio</span><span class="o">.</span><span class="n">display_label</span> <span class="o">==</span> <span class="s2">"-"</span><span class="p">:</span>
352 | <span class="k">return</span> <span class="sa">f</span><span class="s2">"-</span><span class="si">{</span><span class="n">radio</span><span class="o">.</span><span class="n">label</span><span class="si">}</span><span class="s2">"</span>
353 | <span class="k">return</span> <span class="s2">""</span>
354 | <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"site"</span><span class="p">:</span>
355 | <span class="k">return</span> <span class="n">radio</span><span class="o">.</span><span class="n">label</span> <span class="c1"># or site ID/URL however you want to identify it</span>
356 | <span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"crawler"</span><span class="p">:</span>
357 | <span class="k">return</span> <span class="n">radio</span><span class="o">.</span><span class="n">label</span>
358 | <span class="k">return</span> <span class="s2">""</span>
359 |
360 | <div class="viewcode-block" id="InputRadioGroup.calculate_group_width">
361 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.calculate_group_width">[docs]</a>
362 | <span class="k">def</span> <span class="nf">calculate_group_width</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
363 | <span class="w"> </span><span class="sd">"""</span>
364 | <span class="sd"> Calculate the display width needed for a radio group.</span>
365 | <span class="sd"> """</span>
366 | <span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">:</span>
367 | <span class="k">return</span> <span class="n">DEFAULT_GROUP_WIDTH</span>
368 | <span class="k">return</span> <span class="nb">max</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">radio</span><span class="o">.</span><span class="n">label</span><span class="p">)</span> <span class="k">for</span> <span class="n">radio</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">)</span></div>
369 |
370 |
371 | <div class="viewcode-block" id="InputRadioGroup.clear">
372 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.clear">[docs]</a>
373 | <span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
374 | <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">:</span>
375 | <span class="n">r</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="mi">0</span></div>
376 |
377 |
378 | <div class="viewcode-block" id="InputRadioGroup.set_layout_constraints">
379 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.set_layout_constraints">[docs]</a>
380 | <span class="k">def</span> <span class="nf">set_layout_constraints</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">available_width</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">available_height</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">is_constrained</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
381 | <span class="w"> </span><span class="sd">"""</span>
382 | <span class="sd"> Set layout constraints for grid-based groups (like sites).</span>
383 | <span class="sd"> </span>
384 | <span class="sd"> Args:</span>
385 | <span class="sd"> available_width: Available horizontal space</span>
386 | <span class="sd"> available_height: Available vertical space </span>
387 | <span class="sd"> is_constrained: Whether layout is constrained (affects sites per column)</span>
388 | <span class="sd"> """</span>
389 | <span class="bp">self</span><span class="o">.</span><span class="n">__available_width</span> <span class="o">=</span> <span class="n">available_width</span>
390 | <span class="bp">self</span><span class="o">.</span><span class="n">__available_height</span> <span class="o">=</span> <span class="n">available_height</span>
391 | <span class="bp">self</span><span class="o">.</span><span class="n">__is_constrained</span> <span class="o">=</span> <span class="n">is_constrained</span>
392 |
393 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s2">"site"</span><span class="p">:</span>
394 | <span class="bp">self</span><span class="o">.</span><span class="n">__calculate_grid_layout</span><span class="p">()</span></div>
395 |
396 |
397 | <div class="viewcode-block" id="InputRadioGroup.get_grid_position">
398 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.get_grid_position">[docs]</a>
399 | <span class="k">def</span> <span class="nf">get_grid_position</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">radio_index</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
400 | <span class="w"> </span><span class="sd">"""</span>
401 | <span class="sd"> Convert linear radio index to grid position.</span>
402 | <span class="sd"> Only applies to site groups; other groups return (radio_index, 0).</span>
403 | <span class="sd"> </span>
404 | <span class="sd"> Args:</span>
405 | <span class="sd"> radio_index: Linear index in radios list</span>
406 | <span class="sd"> </span>
407 | <span class="sd"> Returns:</span>
408 | <span class="sd"> tuple: (row, column) position in grid layout</span>
409 | <span class="sd"> """</span>
410 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
411 | <span class="k">return</span> <span class="p">(</span><span class="n">radio_index</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
412 |
413 | <span class="n">row</span> <span class="o">=</span> <span class="n">radio_index</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span>
414 | <span class="n">col</span> <span class="o">=</span> <span class="n">radio_index</span> <span class="o">//</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span>
415 | <span class="k">return</span> <span class="p">(</span><span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">)</span></div>
416 |
417 |
418 | <div class="viewcode-block" id="InputRadioGroup.get_index_from_grid">
419 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.get_index_from_grid">[docs]</a>
420 | <span class="k">def</span> <span class="nf">get_index_from_grid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">row</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">col</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
421 | <span class="w"> </span><span class="sd">"""</span>
422 | <span class="sd"> Convert grid position to linear radio index.</span>
423 | <span class="sd"> Only works for site groups; returns None for other group types.</span>
424 | <span class="sd"> </span>
425 | <span class="sd"> Args:</span>
426 | <span class="sd"> row: Row in grid (0-based)</span>
427 | <span class="sd"> col: Column in grid (0-based)</span>
428 | <span class="sd"> </span>
429 | <span class="sd"> Returns:</span>
430 | <span class="sd"> Linear index if position exists within grid bounds, None otherwise</span>
431 | <span class="sd"> """</span>
432 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span><span class="p">:</span>
433 | <span class="k">return</span> <span class="n">row</span> <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">row</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
434 | <span class="c1">#</span>
435 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
436 | <span class="k">return</span> <span class="kc">None</span>
437 | <span class="c1">#</span>
438 | <span class="n">radio_index</span> <span class="o">=</span> <span class="n">col</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span> <span class="o">+</span> <span class="n">row</span>
439 | <span class="k">if</span> <span class="p">(</span><span class="mi">0</span> <span class="o"><=</span> <span class="n">radio_index</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">)</span> <span class="ow">and</span>
440 | <span class="n">radio_index</span> <span class="o"><</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">__max_columns</span><span class="p">):</span>
441 | <span class="k">return</span> <span class="n">radio_index</span>
442 | <span class="k">return</span> <span class="kc">None</span></div>
443 |
444 |
445 | <div class="viewcode-block" id="InputRadioGroup.navigate_left">
446 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.navigate_left">[docs]</a>
447 | <span class="k">def</span> <span class="nf">navigate_left</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">current_radio_index</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
448 | <span class="w"> </span><span class="sd">"""</span>
449 | <span class="sd"> Navigate left within this group's layout.</span>
450 | <span class="sd"> </span>
451 | <span class="sd"> Args:</span>
452 | <span class="sd"> current_radio_index: Current position in radios list</span>
453 | <span class="sd"> </span>
454 | <span class="sd"> Returns:</span>
455 | <span class="sd"> New radio index if navigation successful, None if should exit group</span>
456 | <span class="sd"> """</span>
457 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span><span class="p">:</span>
458 | <span class="c1"># don't support horizontal navigation</span>
459 | <span class="k">return</span> <span class="kc">None</span>
460 |
461 | <span class="n">current_row</span><span class="p">,</span> <span class="n">current_col</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_grid_position</span><span class="p">(</span><span class="n">current_radio_index</span><span class="p">)</span>
462 |
463 | <span class="k">if</span> <span class="n">current_col</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
464 | <span class="c1"># to previous column, same row</span>
465 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_index_from_grid</span><span class="p">(</span><span class="n">current_row</span><span class="p">,</span> <span class="n">current_col</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
466 | <span class="k">else</span><span class="p">:</span>
467 | <span class="c1"># at leftmost column, signal exit to parent</span>
468 | <span class="k">return</span> <span class="kc">None</span></div>
469 |
470 |
471 | <div class="viewcode-block" id="InputRadioGroup.navigate_right">
472 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.navigate_right">[docs]</a>
473 | <span class="k">def</span> <span class="nf">navigate_right</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">current_radio_index</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
474 | <span class="w"> </span><span class="sd">"""</span>
475 | <span class="sd"> Navigate right within this group's layout.</span>
476 | <span class="sd"> </span>
477 | <span class="sd"> Args:</span>
478 | <span class="sd"> current_radio_index: Current position in radios list</span>
479 | <span class="sd"> </span>
480 | <span class="sd"> Returns:</span>
481 | <span class="sd"> New radio index if navigation successful, None if should exit group</span>
482 | <span class="sd"> """</span>
483 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span><span class="p">:</span>
484 | <span class="c1"># don't support horizontal navigation</span>
485 | <span class="k">return</span> <span class="kc">None</span>
486 |
487 | <span class="n">current_row</span><span class="p">,</span> <span class="n">current_col</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_grid_position</span><span class="p">(</span><span class="n">current_radio_index</span><span class="p">)</span>
488 | <span class="n">new_index</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_index_from_grid</span><span class="p">(</span><span class="n">current_row</span><span class="p">,</span> <span class="n">current_col</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
489 | <span class="k">return</span> <span class="n">new_index</span> <span class="c1"># if invalid/out of bounds</span></div>
490 |
491 |
492 | <div class="viewcode-block" id="InputRadioGroup.navigate_to_row">
493 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.navigate_to_row">[docs]</a>
494 | <span class="k">def</span> <span class="nf">navigate_to_row</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target_row</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">from_column</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
495 | <span class="w"> </span><span class="sd">"""</span>
496 | <span class="sd"> Navigate to a specific row from an external column position.</span>
497 | <span class="sd"> """</span>
498 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span><span class="p">:</span>
499 | <span class="k">return</span> <span class="n">target_row</span> <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">target_row</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
500 |
501 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
502 | <span class="k">return</span> <span class="n">target_row</span> <span class="k">if</span> <span class="mi">0</span> <span class="o"><=</span> <span class="n">target_row</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">)</span> <span class="k">else</span> <span class="kc">None</span>
503 |
504 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_index_from_grid</span><span class="p">(</span><span class="n">target_row</span><span class="p">,</span> <span class="n">from_column</span><span class="p">)</span></div>
505 |
506 |
507 | <div class="viewcode-block" id="InputRadioGroup.get_row_from_index">
508 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioGroup.get_row_from_index">[docs]</a>
509 | <span class="k">def</span> <span class="nf">get_row_from_index</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">radio_index</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
510 | <span class="w"> </span><span class="sd">"""</span>
511 | <span class="sd"> Get the row number for navigation between groups.</span>
512 | <span class="sd"> </span>
513 | <span class="sd"> Args:</span>
514 | <span class="sd"> radio_index: Linear index in radios list</span>
515 | <span class="sd"> </span>
516 | <span class="sd"> Returns:</span>
517 | <span class="sd"> Row number for inter-group navigation</span>
518 | <span class="sd"> """</span>
519 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span><span class="p">:</span>
520 | <span class="k">return</span> <span class="n">radio_index</span>
521 |
522 | <span class="n">row</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_grid_position</span><span class="p">(</span><span class="n">radio_index</span><span class="p">)</span>
523 | <span class="k">return</span> <span class="n">row</span></div>
524 |
525 |
526 | <span class="k">def</span> <span class="nf">__calculate_grid_layout</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
527 | <span class="w"> </span><span class="sd">"""</span>
528 | <span class="sd"> Calculate grid layout parameters for sites group.</span>
529 | <span class="sd"> """</span>
530 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">!=</span> <span class="s2">"site"</span><span class="p">:</span>
531 | <span class="k">return</span>
532 |
533 | <span class="bp">self</span><span class="o">.</span><span class="n">__sites_per_column</span> <span class="o">=</span> <span class="p">(</span><span class="n">LAYOUT_CONSTRAINED_SITES_PER_COLUMN</span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__is_constrained</span>
534 | <span class="k">else</span> <span class="nb">min</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__available_height</span> <span class="o">-</span> <span class="n">LAYOUT_SITES_GRID_OFFSET</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">radios</span><span class="p">)))</span>
535 |
536 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">__available_width</span> <span class="o">></span> <span class="n">SITE_COLUMN_WIDTH</span><span class="p">:</span>
537 | <span class="bp">self</span><span class="o">.</span><span class="n">__max_columns</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__available_width</span> <span class="o">//</span> <span class="p">(</span><span class="n">SITE_COLUMN_WIDTH</span> <span class="o">+</span> <span class="n">LAYOUT_GRID_COLUMN_SPACING</span><span class="p">))</span>
538 | <span class="k">else</span><span class="p">:</span>
539 | <span class="bp">self</span><span class="o">.</span><span class="n">__max_columns</span> <span class="o">=</span> <span class="mi">1</span>
540 |
541 | <span class="k">def</span> <span class="nf">__display_url</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
542 | <span class="k">return</span> <span class="n">url</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"://"</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">rstrip</span><span class="p">(</span><span class="s2">"/"</span><span class="p">)</span>
543 |
544 | <span class="k">def</span> <span class="nf">__get_on_off_state</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">:</span>
545 | <span class="k">return</span> <span class="p">[</span>
546 | <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">""</span><span class="p">),</span>
547 | <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">"●"</span><span class="p">,</span> <span class="s2">"on"</span><span class="p">)</span>
548 | <span class="p">]</span>
549 |
550 | <span class="k">def</span> <span class="nf">__load_crawlers</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
551 | <span class="c1"># "archivebox", "httrack", "interrobot", "katana", "siteone", "warc", "wget"</span>
552 | <span class="bp">self</span><span class="o">.</span><span class="n">radios</span> <span class="o">=</span> <span class="p">[</span>
553 | <span class="n">InputRadio</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"crawler"</span><span class="p">,</span> <span class="n">label</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__get_on_off_state</span><span class="p">())</span>
554 | <span class="k">for</span> <span class="n">label</span> <span class="ow">in</span> <span class="n">VALID_CRAWLER_CHOICES</span>
555 | <span class="p">]</span>
556 |
557 | <span class="k">def</span> <span class="nf">__load_filters</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
558 | <span class="bp">self</span><span class="o">.</span><span class="n">radios</span> <span class="o">=</span> <span class="p">[</span>
559 | <span class="n">InputRadio</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"filter"</span><span class="p">,</span> <span class="s2">"HTML"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__get_on_off_state</span><span class="p">()),</span>
560 | <span class="n">InputRadio</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"filter"</span><span class="p">,</span> <span class="s2">"any"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__get_on_off_state</span><span class="p">())</span>
561 | <span class="p">]</span>
562 |
563 | <span class="k">def</span> <span class="nf">__load_sites</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">sites</span><span class="p">:</span> <span class="nb">list</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
564 | <span class="n">site_labels</span> <span class="o">=</span> <span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">__display_url</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">urls</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">urls</span> <span class="k">else</span> <span class="s2">"unknown"</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">sites</span><span class="p">]</span>
565 | <span class="bp">self</span><span class="o">.</span><span class="n">radios</span> <span class="o">=</span> <span class="p">[</span>
566 | <span class="n">InputRadio</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"site"</span><span class="p">,</span> <span class="n">label</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__get_on_off_state</span><span class="p">())</span>
567 | <span class="k">for</span> <span class="n">label</span> <span class="ow">in</span> <span class="n">site_labels</span>
568 | <span class="p">]</span>
569 |
570 | <span class="k">def</span> <span class="nf">__load_sorts</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
571 | <span class="n">sort_states</span> <span class="o">=</span> <span class="p">[</span>
572 | <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">""</span><span class="p">),</span>
573 | <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">"+"</span><span class="p">,</span> <span class="s2">"+"</span><span class="p">),</span>
574 | <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">"-"</span><span class="p">,</span> <span class="s2">"-"</span><span class="p">)</span>
575 | <span class="p">]</span>
576 |
577 | <span class="n">sort_labels</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"URL"</span><span class="p">,</span> <span class="s2">"status"</span><span class="p">,</span> <span class="s2">"size"</span><span class="p">]</span>
578 |
579 | <span class="bp">self</span><span class="o">.</span><span class="n">radios</span> <span class="o">=</span> <span class="p">[</span>
580 | <span class="n">InputRadio</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="s2">"sort"</span><span class="p">,</span> <span class="n">label</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">sort_states</span><span class="o">.</span><span class="n">copy</span><span class="p">())</span>
581 | <span class="k">for</span> <span class="n">label</span> <span class="ow">in</span> <span class="n">sort_labels</span>
582 | <span class="p">]</span></div>
583 |
584 |
585 | <div class="viewcode-block" id="InputRadioState">
586 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputRadioState">[docs]</a>
587 | <span class="k">class</span> <span class="nc">InputRadioState</span><span class="p">(</span><span class="n">NamedTuple</span><span class="p">):</span>
588 | <span class="n">label</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># "●", " ", "-", "+"</span>
589 | <span class="n">value</span><span class="p">:</span> <span class="nb">str</span> <span class="c1"># "", "+url", "-sort"</span></div>
590 |
591 |
592 | <div class="viewcode-block" id="InputText">
593 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText">[docs]</a>
594 | <span class="k">class</span> <span class="nc">InputText</span><span class="p">:</span>
595 | <span class="w"> </span><span class="sd">"""</span>
596 | <span class="sd"> A reusable text input field with cursor management, rendering, and input handling.</span>
597 | <span class="sd"> Consolidates the common text input functionality used across the application.</span>
598 | <span class="sd"> """</span>
599 |
600 | <div class="viewcode-block" id="InputText.__init__">
601 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.__init__">[docs]</a>
602 | <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">initial_value</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span><span class="p">,</span> <span class="n">max_length</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">label</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">""</span><span class="p">):</span>
603 | <span class="w"> </span><span class="sd">"""</span>
604 | <span class="sd"> Initialize the text input field.</span>
605 | <span class="sd"> </span>
606 | <span class="sd"> Args:</span>
607 | <span class="sd"> initial_value: Starting text value</span>
608 | <span class="sd"> max_length: Maximum allowed text length (None for unlimited)</span>
609 | <span class="sd"> label: Display label for the field</span>
610 | <span class="sd"> """</span>
611 | <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">initial_value</span>
612 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">initial_value</span><span class="p">)</span>
613 | <span class="bp">self</span><span class="o">.</span><span class="n">max_length</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">max_length</span>
614 | <span class="bp">self</span><span class="o">.</span><span class="n">label</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">label</span>
615 |
616 | <span class="bp">self</span><span class="o">.</span><span class="n">_last_display_cache</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">tuple</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span>
617 | <span class="bp">self</span><span class="o">.</span><span class="n">_last_value_hash</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span></div>
618 |
619 |
620 | <div class="viewcode-block" id="InputText.backspace">
621 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.backspace">[docs]</a>
622 | <span class="k">def</span> <span class="nf">backspace</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
623 | <span class="w"> </span><span class="sd">"""</span>
624 | <span class="sd"> Remove the character before the cursor.</span>
625 | <span class="sd"> """</span>
626 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
627 | <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[:</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">:]</span>
628 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">-=</span> <span class="mi">1</span></div>
629 |
630 |
631 | <div class="viewcode-block" id="InputText.clear">
632 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.clear">[docs]</a>
633 | <span class="k">def</span> <span class="nf">clear</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
634 | <span class="w"> </span><span class="sd">"""</span>
635 | <span class="sd"> Clear all text and reset cursor.</span>
636 | <span class="sd"> """</span>
637 | <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="s2">""</span>
638 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">=</span> <span class="mi">0</span></div>
639 |
640 |
641 | <div class="viewcode-block" id="InputText.delete">
642 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.delete">[docs]</a>
643 | <span class="k">def</span> <span class="nf">delete</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
644 | <span class="w"> </span><span class="sd">"""</span>
645 | <span class="sd"> Remove the character at the cursor position.</span>
646 | <span class="sd"> """</span>
647 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">):</span>
648 | <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[:</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">]</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">+</span> <span class="mi">1</span><span class="p">:]</span></div>
649 |
650 |
651 | <div class="viewcode-block" id="InputText.end">
652 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.end">[docs]</a>
653 | <span class="k">def</span> <span class="nf">end</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
654 | <span class="w"> </span><span class="sd">"""</span>
655 | <span class="sd"> Move cursor to the end of the text.</span>
656 | <span class="sd"> """</span>
657 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span></div>
658 |
659 |
660 | <div class="viewcode-block" id="InputText.handle_input">
661 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.handle_input">[docs]</a>
662 | <span class="k">def</span> <span class="nf">handle_input</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
663 | <span class="w"> </span><span class="sd">"""</span>
664 | <span class="sd"> Handle keyboard input for the text field.</span>
665 | <span class="sd"> </span>
666 | <span class="sd"> Args:</span>
667 | <span class="sd"> key: The curses key code</span>
668 | <span class="sd"> </span>
669 | <span class="sd"> Returns:</span>
670 | <span class="sd"> bool: True if the input was handled, False otherwise</span>
671 | <span class="sd"> """</span>
672 | <span class="n">handlers</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">callable</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
673 | <span class="n">curses</span><span class="o">.</span><span class="n">KEY_LEFT</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">move_cursor_left</span><span class="p">,</span>
674 | <span class="n">curses</span><span class="o">.</span><span class="n">KEY_RIGHT</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">move_cursor_right</span><span class="p">,</span>
675 | <span class="n">curses</span><span class="o">.</span><span class="n">KEY_HOME</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">home</span><span class="p">,</span>
676 | <span class="n">curses</span><span class="o">.</span><span class="n">KEY_END</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">end</span><span class="p">,</span>
677 | <span class="n">curses</span><span class="o">.</span><span class="n">KEY_BACKSPACE</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">backspace</span><span class="p">,</span>
678 | <span class="mi">127</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">backspace</span><span class="p">,</span> <span class="c1"># alternative backspace</span>
679 | <span class="mi">8</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">backspace</span><span class="p">,</span> <span class="c1"># alternative backspace</span>
680 | <span class="n">curses</span><span class="o">.</span><span class="n">KEY_DC</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">delete</span><span class="p">,</span>
681 | <span class="p">}</span>
682 |
683 | <span class="n">handler</span> <span class="o">=</span> <span class="n">handlers</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
684 | <span class="k">if</span> <span class="n">handler</span><span class="p">:</span>
685 | <span class="n">handler</span><span class="p">()</span>
686 | <span class="k">return</span> <span class="kc">True</span>
687 |
688 | <span class="k">if</span> <span class="mi">32</span> <span class="o"><=</span> <span class="n">key</span> <span class="o"><=</span> <span class="mi">126</span><span class="p">:</span> <span class="c1"># printable characters</span>
689 | <span class="n">char</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
690 | <span class="bp">self</span><span class="o">.</span><span class="n">insert_char</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
691 | <span class="k">return</span> <span class="kc">True</span>
692 |
693 | <span class="k">return</span> <span class="kc">False</span></div>
694 |
695 |
696 | <div class="viewcode-block" id="InputText.home">
697 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.home">[docs]</a>
698 | <span class="k">def</span> <span class="nf">home</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
699 | <span class="w"> </span><span class="sd">"""</span>
700 | <span class="sd"> Move cursor to the beginning of the text.</span>
701 | <span class="sd"> """</span>
702 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">=</span> <span class="mi">0</span></div>
703 |
704 |
705 | <div class="viewcode-block" id="InputText.insert_char">
706 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.insert_char">[docs]</a>
707 | <span class="k">def</span> <span class="nf">insert_char</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">char</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
708 | <span class="w"> </span><span class="sd">"""</span>
709 | <span class="sd"> Insert a character at the current cursor position.</span>
710 | <span class="sd"> """</span>
711 | <span class="n">sanitized</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__sanitize_input</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
712 | <span class="k">if</span> <span class="n">sanitized</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
713 | <span class="k">return</span>
714 |
715 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_length</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o">>=</span> <span class="bp">self</span><span class="o">.</span><span class="n">max_length</span><span class="p">:</span>
716 | <span class="k">return</span>
717 |
718 | <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[:</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">]</span> <span class="o">+</span> <span class="n">char</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">:]</span>
719 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">+=</span> <span class="mi">1</span></div>
720 |
721 |
722 | <div class="viewcode-block" id="InputText.is_empty">
723 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.is_empty">[docs]</a>
724 | <span class="k">def</span> <span class="nf">is_empty</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
725 | <span class="w"> </span><span class="sd">"""</span>
726 | <span class="sd"> Check if the text field is empty.</span>
727 | <span class="sd"> """</span>
728 | <span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">strip</span><span class="p">())</span> <span class="o">==</span> <span class="mi">0</span></div>
729 |
730 |
731 | <div class="viewcode-block" id="InputText.move_cursor_left">
732 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.move_cursor_left">[docs]</a>
733 | <span class="k">def</span> <span class="nf">move_cursor_left</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
734 | <span class="w"> </span><span class="sd">"""</span>
735 | <span class="sd"> Move cursor one position to the left.</span>
736 | <span class="sd"> """</span>
737 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
738 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">-=</span> <span class="mi">1</span></div>
739 |
740 |
741 | <div class="viewcode-block" id="InputText.move_cursor_right">
742 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.move_cursor_right">[docs]</a>
743 | <span class="k">def</span> <span class="nf">move_cursor_right</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
744 | <span class="w"> </span><span class="sd">"""</span>
745 | <span class="sd"> Move cursor one position to the right.</span>
746 | <span class="sd"> """</span>
747 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">):</span>
748 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">+=</span> <span class="mi">1</span></div>
749 |
750 |
751 | <div class="viewcode-block" id="InputText.render">
752 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.render">[docs]</a>
753 | <span class="k">def</span> <span class="nf">render</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stdscr</span><span class="p">:</span> <span class="n">curses</span><span class="o">.</span><span class="n">window</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">width</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
754 | <span class="n">focused</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span> <span class="n">style</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
755 | <span class="w"> </span><span class="sd">"""</span>
756 | <span class="sd"> Render the text input field with box, text, and cursor.</span>
757 | <span class="sd"> </span>
758 | <span class="sd"> Args:</span>
759 | <span class="sd"> stdscr: The curses window</span>
760 | <span class="sd"> y: Y position to render at</span>
761 | <span class="sd"> x: X position to render at </span>
762 | <span class="sd"> width: Total width of the input box</span>
763 | <span class="sd"> focused: Whether this field has focus (shows cursor)</span>
764 | <span class="sd"> style: Curses style attributes to apply</span>
765 | <span class="sd"> """</span>
766 |
767 | <span class="c1"># account for [ ] brackets</span>
768 | <span class="n">inner_width</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">width</span> <span class="o">-</span> <span class="n">INPUT_BOX_BRACKET_WIDTH</span><span class="p">)</span>
769 | <span class="n">display_text</span><span class="p">,</span> <span class="n">display_cursor_pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">__calculate_display_text_and_cursor</span><span class="p">(</span><span class="n">inner_width</span><span class="p">)</span>
770 | <span class="n">box_content</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"[</span><span class="si">{</span><span class="n">display_text</span><span class="o">.</span><span class="n">ljust</span><span class="p">(</span><span class="n">inner_width</span><span class="p">)</span><span class="si">}</span><span class="s2">]"</span>
771 | <span class="k">if</span> <span class="n">style</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
772 | <span class="n">style</span> <span class="o">=</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_REVERSE</span> <span class="k">if</span> <span class="n">focused</span> <span class="k">else</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_NORMAL</span>
773 |
774 | <span class="n">safe_addstr</span><span class="p">(</span><span class="n">stdscr</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">box_content</span><span class="p">,</span> <span class="n">style</span><span class="p">)</span>
775 | <span class="k">if</span> <span class="n">focused</span><span class="p">:</span>
776 | <span class="bp">self</span><span class="o">.</span><span class="n">__render_cursor</span><span class="p">(</span><span class="n">stdscr</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">display_text</span><span class="p">,</span> <span class="n">display_cursor_pos</span><span class="p">,</span> <span class="n">inner_width</span><span class="p">)</span></div>
777 |
778 |
779 | <div class="viewcode-block" id="InputText.set_value">
780 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.InputText.set_value">[docs]</a>
781 | <span class="k">def</span> <span class="nf">set_value</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">new_value</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
782 | <span class="w"> </span><span class="sd">"""</span>
783 | <span class="sd"> Set the text value and adjust cursor if needed.</span>
784 | <span class="sd"> """</span>
785 | <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">new_value</span>
786 | <span class="c1"># cursor doesn't go beyond text length</span>
787 | <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">))</span></div>
788 |
789 |
790 | <span class="k">def</span> <span class="nf">__sanitize_input</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">char</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
791 | <span class="w"> </span><span class="sd">"""</span>
792 | <span class="sd"> Sanitize input character, return None if should be rejected</span>
793 | <span class="sd"> """</span>
794 | <span class="c1"># strip control characters</span>
795 | <span class="k">if</span> <span class="nb">ord</span><span class="p">(</span><span class="n">char</span><span class="p">)</span> <span class="o"><</span> <span class="mi">32</span> <span class="ow">or</span> <span class="nb">ord</span><span class="p">(</span><span class="n">char</span><span class="p">)</span> <span class="o">==</span> <span class="mi">127</span><span class="p">:</span>
796 | <span class="k">return</span> <span class="kc">None</span>
797 | <span class="c1"># add more checks here as needed</span>
798 | <span class="k">return</span> <span class="n">char</span>
799 |
800 | <span class="k">def</span> <span class="nf">__render_cursor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">stdscr</span><span class="p">:</span> <span class="n">curses</span><span class="o">.</span><span class="n">window</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
801 | <span class="n">display_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">display_cursor_pos</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">inner_width</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
802 | <span class="w"> </span><span class="sd">"""</span>
803 | <span class="sd"> Render the cursor at the appropriate position.</span>
804 | <span class="sd"> </span>
805 | <span class="sd"> Args:</span>
806 | <span class="sd"> stdscr: The curses window</span>
807 | <span class="sd"> y: Y position of the input box</span>
808 | <span class="sd"> x: X position of the input box</span>
809 | <span class="sd"> display_text: The currently displayed text</span>
810 | <span class="sd"> display_cursor_pos: Where the cursor appears in the displayed text</span>
811 | <span class="sd"> inner_width: Available width inside the box</span>
812 | <span class="sd"> """</span>
813 |
814 | <span class="k">try</span><span class="p">:</span>
815 | <span class="k">if</span> <span class="n">display_cursor_pos</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">display_text</span><span class="p">)</span> <span class="ow">and</span> <span class="n">display_cursor_pos</span> <span class="o"><</span> <span class="n">inner_width</span><span class="p">:</span>
816 | <span class="n">cursor_x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">display_cursor_pos</span>
817 | <span class="c1"># highlight the character under cursor instead of just reversing</span>
818 | <span class="n">char_under_cursor</span> <span class="o">=</span> <span class="n">display_text</span><span class="p">[</span><span class="n">display_cursor_pos</span><span class="p">]</span>
819 | <span class="n">safe_addstr</span><span class="p">(</span><span class="n">stdscr</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">cursor_x</span><span class="p">,</span> <span class="n">char_under_cursor</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_REVERSE</span> <span class="o">|</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_BOLD</span><span class="p">)</span>
820 | <span class="k">elif</span> <span class="n">display_cursor_pos</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">display_cursor_pos</span> <span class="o"><</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">inner_width</span><span class="p">:</span>
821 | <span class="c1"># cursor at end - underscore</span>
822 | <span class="n">cursor_x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">display_cursor_pos</span>
823 | <span class="n">safe_addstr</span><span class="p">(</span><span class="n">stdscr</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">cursor_x</span><span class="p">,</span> <span class="s1">'_'</span><span class="p">,</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_REVERSE</span> <span class="o">|</span> <span class="n">curses</span><span class="o">.</span><span class="n">A_BOLD</span><span class="p">)</span>
824 | <span class="k">except</span> <span class="n">curses</span><span class="o">.</span><span class="n">error</span><span class="p">:</span>
825 | <span class="k">pass</span>
826 |
827 |
828 | <span class="k">def</span> <span class="nf">__calculate_display_text_and_cursor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">inner_width</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">int</span><span class="p">]:</span>
829 | <span class="w"> </span><span class="sd">"""</span>
830 | <span class="sd"> Calculate what portion of text to display and where the cursor should appear.</span>
831 | <span class="sd"> Handles horizontal scrolling for long text.</span>
832 | <span class="sd"> </span>
833 | <span class="sd"> Args:</span>
834 | <span class="sd"> inner_width: Available width inside the input box</span>
835 | <span class="sd"> </span>
836 | <span class="sd"> Returns:</span>
837 | <span class="sd"> tuple: (display_text, display_cursor_position)</span>
838 | <span class="sd"> """</span>
839 | <span class="n">current_hash</span> <span class="o">=</span> <span class="nb">hash</span><span class="p">((</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">,</span> <span class="n">inner_width</span><span class="p">))</span>
840 | <span class="k">if</span> <span class="n">current_hash</span> <span class="o">==</span> <span class="bp">self</span><span class="o">.</span><span class="n">_last_value_hash</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_last_display_cache</span><span class="p">:</span>
841 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_last_display_cache</span>
842 |
843 | <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o"><=</span> <span class="n">inner_width</span><span class="p">:</span>
844 | <span class="c1"># text fits entirely</span>
845 | <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span>
846 |
847 | <span class="c1"># text is longer than available space, scroll</span>
848 | <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">>=</span> <span class="n">inner_width</span> <span class="o">-</span> <span class="n">CURSOR_SCROLL_THRESHOLD</span><span class="p">:</span>
849 | <span class="n">start_pos</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> <span class="o">-</span> <span class="n">inner_width</span><span class="p">)</span>
850 | <span class="n">display_text</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[</span><span class="n">start_pos</span><span class="p">:]</span>
851 | <span class="n">display_cursor_pos</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span> <span class="o">-</span> <span class="n">start_pos</span>
852 | <span class="k">else</span><span class="p">:</span>
853 | <span class="n">display_text</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">[:</span><span class="n">inner_width</span><span class="p">]</span>
854 | <span class="n">display_cursor_pos</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">cursor_pos</span><span class="p">,</span> <span class="n">inner_width</span><span class="p">)</span>
855 |
856 | <span class="k">return</span> <span class="n">display_text</span><span class="p">,</span> <span class="n">display_cursor_pos</span></div>
857 |
858 |
859 | <div class="viewcode-block" id="ViewBounds">
860 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.ViewBounds">[docs]</a>
861 | <span class="k">class</span> <span class="nc">ViewBounds</span><span class="p">:</span>
862 | <div class="viewcode-block" id="ViewBounds.__init__">
863 | <a class="viewcode-back" href="../../../mcp_server_webcrawl.interactive.html#mcp_server_webcrawl.interactive.ui.ViewBounds.__init__">[docs]</a>
864 | <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">width</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">height</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">0</span><span class="p">):</span>
865 | <span class="bp">self</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
866 | <span class="bp">self</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">y</span>
867 | <span class="bp">self</span><span class="o">.</span><span class="n">width</span> <span class="o">=</span> <span class="n">width</span>
868 | <span class="bp">self</span><span class="o">.</span><span class="n">height</span> <span class="o">=</span> <span class="n">height</span></div>
869 | </div>
870 |
871 | </pre></div>
872 |
873 | </div>
874 | </div>
875 | <footer>
876 |
877 | <hr/>
878 |
879 | <div role="contentinfo">
880 | <p>© Copyright 2025, pragmar.</p>
881 | </div>
882 |
883 | Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
884 | <a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
885 | provided by <a href="https://readthedocs.org">Read the Docs</a>.
886 |
887 |
888 | </footer>
889 | </div>
890 | </div>
891 | </section>
892 | </div>
893 | <script>
894 | jQuery(function () {
895 | SphinxRtdTheme.Navigation.enable(true);
896 | });
897 | </script>
898 |
899 | </body>
900 | </html>
```