#
tokens: 40395/50000 1/216 files (page 29/35)
lines: on (toggle) GitHub
raw markdown copy reset
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 &mdash; 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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
165 | <span class="w">    </span><span class="sd">&quot;&quot;&quot;</span>
166 | <span class="sd">    Safe addstr that handles screen edge errors.</span>
167 | <span class="sd">    &quot;&quot;&quot;</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">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&quot;states must be provided and non-empty&quot;</span>
194 |         <span class="k">assert</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">index</span> <span class="o">&lt;</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">&quot;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&quot;</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">&quot;&quot;&quot;</span>
207 | <span class="sd">        Get the current state</span>
208 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
213 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
214 | <span class="sd">        Get the current display label</span>
215 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
220 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
221 | <span class="sd">        Get the current value</span>
222 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
228 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
229 | <span class="sd">        Cycle to the next state</span>
230 | <span class="sd">        &quot;&quot;&quot;</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">&quot;filter&quot;</span><span class="p">,</span> <span class="s2">&quot;site&quot;</span><span class="p">,</span> <span class="s2">&quot;crawler&quot;</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">&quot;sort&quot;</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 &quot; &quot; -&gt; active ascending &quot;+&quot;</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 &quot;+&quot; -&gt; descending &quot;-&quot;</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 &quot;-&quot; -&gt; ascending &quot;+&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
251 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
252 | <span class="sd">        Render a single radio option.</span>
253 | <span class="sd">        &quot;&quot;&quot;</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">&gt;</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">&quot;…&quot;</span>
259 | 
260 |         <span class="n">line</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">&quot;(</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">&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
272 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
273 | <span class="sd">        Set the current state by index</span>
274 | <span class="sd">        &quot;&quot;&quot;</span>
275 |         <span class="k">if</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="n">index</span> <span class="o">&lt;</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">&quot;State index </span><span class="si">{</span><span class="n">index</span><span class="si">}</span><span class="s2"> out of range&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
284 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
285 | <span class="sd">        Set the current state by index</span>
286 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
291 |         <span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;</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">)&quot;</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">&quot;&quot;&quot;</span>
298 | <span class="sd">    Radio group with navigation and layout management capabilities.</span>
299 | <span class="sd">    &quot;&quot;&quot;</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">&quot;&quot;&quot;</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 (&quot;filter&quot;, &quot;sort&quot;, &quot;site&quot;, or &quot;crawler&quot;)</span>
308 | <span class="sd">            sites: List of SiteResult objects, required only for &quot;site&quot; group type</span>
309 | <span class="sd">        &quot;&quot;&quot;</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">&quot;filter&quot;</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">&quot;Filter:&quot;</span><span class="p">),</span>
326 |             <span class="s2">&quot;site&quot;</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">&quot;Sites:&quot;</span><span class="p">),</span>
327 |             <span class="s2">&quot;sort&quot;</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">&quot;Sorts:&quot;</span><span class="p">),</span>
328 |             <span class="s2">&quot;crawler&quot;</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">&quot;Crawlers:&quot;</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">&quot;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">&quot;</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">-&gt;</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">&quot;on&quot;</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">&quot;+&quot;</span><span class="p">,</span> <span class="s2">&quot;-&quot;</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">&quot;filter&quot;</span><span class="p">:</span>
347 |                     <span class="k">return</span> <span class="s2">&quot;html&quot;</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">&quot;HTML&quot;</span> <span class="k">else</span> <span class="s2">&quot;&quot;</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">&quot;sort&quot;</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">&quot;+&quot;</span><span class="p">:</span>
350 |                         <span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;+</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">&quot;</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">&quot;-&quot;</span><span class="p">:</span>
352 |                         <span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;-</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">&quot;</span>
353 |                     <span class="k">return</span> <span class="s2">&quot;&quot;</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">&quot;site&quot;</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">&quot;crawler&quot;</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">&quot;&quot;</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">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
363 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
364 | <span class="sd">        Calculate the display width needed for a radio group.</span>
365 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
381 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&quot;site&quot;</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">-&gt;</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">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&quot;site&quot;</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">-&gt;</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">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&quot;site&quot;</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">&lt;=</span> <span class="n">row</span> <span class="o">&lt;</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">&lt;=</span> <span class="n">radio_index</span> <span class="o">&lt;</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">&lt;</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">-&gt;</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">&quot;&quot;&quot;</span>
449 | <span class="sd">        Navigate left within this group&#39;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">        &quot;&quot;&quot;</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">&quot;site&quot;</span><span class="p">:</span>
458 |             <span class="c1"># don&#39;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">&gt;</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">-&gt;</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">&quot;&quot;&quot;</span>
475 | <span class="sd">        Navigate right within this group&#39;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">        &quot;&quot;&quot;</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">&quot;site&quot;</span><span class="p">:</span>
484 |             <span class="c1"># don&#39;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">-&gt;</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">&quot;&quot;&quot;</span>
496 | <span class="sd">        Navigate to a specific row from an external column position.</span>
497 | <span class="sd">        &quot;&quot;&quot;</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">&quot;site&quot;</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">&lt;=</span> <span class="n">target_row</span> <span class="o">&lt;</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">&lt;=</span> <span class="n">target_row</span> <span class="o">&lt;</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">-&gt;</span> <span class="nb">int</span><span class="p">:</span>
510 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&quot;site&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
527 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
528 | <span class="sd">        Calculate grid layout parameters for sites group.</span>
529 | <span class="sd">        &quot;&quot;&quot;</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">&quot;site&quot;</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">&gt;</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">-&gt;</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">&quot;://&quot;</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">&quot;/&quot;</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">-&gt;</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">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">),</span>
547 |             <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">&quot;●&quot;</span><span class="p">,</span> <span class="s2">&quot;on&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
551 |         <span class="c1"># &quot;archivebox&quot;, &quot;httrack&quot;, &quot;interrobot&quot;, &quot;katana&quot;, &quot;siteone&quot;, &quot;warc&quot;, &quot;wget&quot;</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">&quot;crawler&quot;</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">-&gt;</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">&quot;filter&quot;</span><span class="p">,</span> <span class="s2">&quot;HTML&quot;</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">&quot;filter&quot;</span><span class="p">,</span> <span class="s2">&quot;any&quot;</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">-&gt;</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">&quot;unknown&quot;</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">&quot;site&quot;</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">-&gt;</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">&quot; &quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">),</span>
573 |             <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">&quot;+&quot;</span><span class="p">,</span> <span class="s2">&quot;+&quot;</span><span class="p">),</span>
574 |             <span class="n">InputRadioState</span><span class="p">(</span><span class="s2">&quot;-&quot;</span><span class="p">,</span> <span class="s2">&quot;-&quot;</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">&quot;URL&quot;</span><span class="p">,</span> <span class="s2">&quot;status&quot;</span><span class="p">,</span> <span class="s2">&quot;size&quot;</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">&quot;sort&quot;</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"># &quot;●&quot;, &quot; &quot;, &quot;-&quot;, &quot;+&quot;</span>
589 |     <span class="n">value</span><span class="p">:</span> <span class="nb">str</span>  <span class="c1"># &quot;&quot;, &quot;+url&quot;, &quot;-sort&quot;</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">&quot;&quot;&quot;</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">    &quot;&quot;&quot;</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">&quot;&quot;</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">&quot;&quot;</span><span class="p">):</span>
603 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
623 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
624 | <span class="sd">        Remove the character before the cursor.</span>
625 | <span class="sd">        &quot;&quot;&quot;</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">&gt;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
634 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
635 | <span class="sd">        Clear all text and reset cursor.</span>
636 | <span class="sd">        &quot;&quot;&quot;</span>
637 |         <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="s2">&quot;&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
644 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
645 | <span class="sd">        Remove the character at the cursor position.</span>
646 | <span class="sd">        &quot;&quot;&quot;</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">&lt;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
654 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
655 | <span class="sd">        Move cursor to the end of the text.</span>
656 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
663 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&lt;=</span> <span class="n">key</span> <span class="o">&lt;=</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
699 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
700 | <span class="sd">        Move cursor to the beginning of the text.</span>
701 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
708 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
709 | <span class="sd">        Insert a character at the current cursor position.</span>
710 | <span class="sd">        &quot;&quot;&quot;</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">&gt;=</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">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
725 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
726 | <span class="sd">        Check if the text field is empty.</span>
727 | <span class="sd">        &quot;&quot;&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
734 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
735 | <span class="sd">        Move cursor one position to the left.</span>
736 | <span class="sd">        &quot;&quot;&quot;</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">&gt;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
744 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
745 | <span class="sd">        Move cursor one position to the right.</span>
746 | <span class="sd">        &quot;&quot;&quot;</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">&lt;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
755 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&quot;[</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">]&quot;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
782 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</span>
783 | <span class="sd">        Set the text value and adjust cursor if needed.</span>
784 | <span class="sd">        &quot;&quot;&quot;</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&#39;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">-&gt;</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">&quot;&quot;&quot;</span>
792 | <span class="sd">        Sanitize input character, return None if should be rejected</span>
793 | <span class="sd">        &quot;&quot;&quot;</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">&lt;</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">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
802 | <span class="w">        </span><span class="sd">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&lt;</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">&lt;</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">&gt;=</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">&lt;</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">&#39;_&#39;</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">-&gt;</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">&quot;&quot;&quot;</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">        &quot;&quot;&quot;</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">&lt;=</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">&gt;=</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>&#169; 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>
```
Page 29/35FirstPrevNextLast