Full Code of darrenburns/posting for AI

main e56508f71b01 cached
141 files
3.1 MB
830.2k tokens
792 symbols
1 requests
Download .txt
Showing preview only (3,319K chars total). Download the full file or copy to clipboard to get everything.
Repository: darrenburns/posting
Branch: main
Commit: e56508f71b01
Files: 141
Total size: 3.1 MB

Directory structure:
gitextract_4f7kaixp/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── coverage.yml
│       ├── docs.yml
│       └── test.yml
├── .gitignore
├── .python-version
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── docs/
│   ├── CHANGELOG.md
│   ├── CNAME
│   ├── faq.md
│   ├── guide/
│   │   ├── collections.md
│   │   ├── command_palette.md
│   │   ├── configuration.md
│   │   ├── environments.md
│   │   ├── external_tools.md
│   │   ├── help_system.md
│   │   ├── importing.md
│   │   ├── index.md
│   │   ├── keymap.md
│   │   ├── navigation.md
│   │   ├── requests.md
│   │   ├── scripting.md
│   │   └── themes.md
│   ├── index.md
│   ├── overrides/
│   │   └── home.html
│   ├── roadmap.md
│   └── stylesheets/
│       └── extra.css
├── mkdocs.yml
├── pyproject.toml
├── src/
│   └── posting/
│       ├── __init__.py
│       ├── __main__.py
│       ├── _start_time.py
│       ├── app.py
│       ├── auth.py
│       ├── collection.py
│       ├── commands.py
│       ├── config.py
│       ├── exit_codes.py
│       ├── files.py
│       ├── help_data.py
│       ├── help_screen.py
│       ├── highlight_url.py
│       ├── highlighters.py
│       ├── importing/
│       │   ├── curl.py
│       │   ├── open_api.py
│       │   └── postman.py
│       ├── jump_overlay.py
│       ├── jumper.py
│       ├── locations.py
│       ├── messages.py
│       ├── posting.scss
│       ├── request_headers.py
│       ├── save_request.py
│       ├── scripts.py
│       ├── suggesters.py
│       ├── themes.py
│       ├── tuple_to_multidict.py
│       ├── types.py
│       ├── urls.py
│       ├── user_host.py
│       ├── variables.py
│       ├── version.py
│       ├── widgets/
│       │   ├── __init__.py
│       │   ├── center_middle.py
│       │   ├── collection/
│       │   │   ├── browser.py
│       │   │   └── new_request_modal.py
│       │   ├── confirmation.py
│       │   ├── datatable.py
│       │   ├── input.py
│       │   ├── key_value.py
│       │   ├── request/
│       │   │   ├── __init__.py
│       │   │   ├── form_editor.py
│       │   │   ├── header_editor.py
│       │   │   ├── method_selection.py
│       │   │   ├── path_editor.py
│       │   │   ├── query_editor.py
│       │   │   ├── request_auth.py
│       │   │   ├── request_body.py
│       │   │   ├── request_editor.py
│       │   │   ├── request_metadata.py
│       │   │   ├── request_options.py
│       │   │   ├── request_scripts.py
│       │   │   └── url_bar.py
│       │   ├── response/
│       │   │   ├── cookies_table.py
│       │   │   ├── response_area.py
│       │   │   ├── response_body.py
│       │   │   ├── response_headers.py
│       │   │   ├── response_trace.py
│       │   │   └── script_output.py
│       │   ├── rich_log.py
│       │   ├── select.py
│       │   ├── tabbed_content.py
│       │   ├── text_area.py
│       │   ├── tree.py
│       │   ├── variable_autocomplete.py
│       │   └── variable_input.py
│       ├── xresources.py
│       └── yaml.py
└── tests/
    ├── posting_snapshot_app.py
    ├── resources/
    │   └── snapshot_report_template.jinja2
    ├── sample-collections/
    │   ├── echo-post-01.posting.yaml
    │   ├── echo.posting.yaml
    │   ├── get-random-user.posting.yaml
    │   ├── jsonplaceholder/
    │   │   ├── posts/
    │   │   │   ├── comments/
    │   │   │   │   ├── edit.posting.yaml
    │   │   │   │   ├── get-comments-query.posting.yaml
    │   │   │   │   └── get-comments.posting.yaml
    │   │   │   ├── create.posting.yaml
    │   │   │   ├── delete.posting.yaml
    │   │   │   ├── get-all.posting.yaml
    │   │   │   └── get-one.posting.yaml
    │   │   ├── todos/
    │   │   │   ├── get-all.posting.yaml
    │   │   │   └── get-one.posting.yaml
    │   │   └── users/
    │   │       ├── create.posting.yaml
    │   │       ├── delete.posting.yaml
    │   │       ├── get-all.posting.yaml
    │   │       ├── get-one.posting.yaml
    │   │       └── update.posting.yaml
    │   └── scripts/
    │       └── my_script.py
    ├── sample-configs/
    │   ├── custom_theme.yaml
    │   ├── custom_theme2.yaml
    │   ├── general.yaml
    │   └── modified_config.yaml
    ├── sample-envs/
    │   ├── sample_base.env
    │   └── sample_extra.env
    ├── sample-importable-collections/
    │   ├── Fixer.postman_collection.json
    │   ├── postman_collection.json
    │   └── test-postman-collection.json
    ├── sample-themes/
    │   ├── another_test.yml
    │   └── serene_ocean.yaml
    ├── test_curl_export.py
    ├── test_curl_import.py
    ├── test_files.py
    ├── test_open_api_import.py
    ├── test_postman_import.py
    ├── test_snapshots.py
    ├── test_urls.py
    └── test_variables.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [darrenburns] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/workflows/coverage.yml
================================================
# This is a follow up job that runs after the CI job has completed.
# It'll post a code coverage comment on pull requests.
name: Post Coverage Comment

on:
  workflow_run:
    workflows: ["Continuous Integration"]
    types:
      - completed

jobs:
  test:
    name: Post Coverage Comment
    runs-on: ubuntu-latest
    if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
    permissions:
      pull-requests: write
      contents: write
      actions: read
    steps:
      # !!! DO NOT run actions/checkout here, for security reasons !!!
      # For details, refer to https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
      - name: Post comment
        uses: py-cov-action/python-coverage-comment-action@v3
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_PR_RUN_ID: ${{ github.event.workflow_run.id }}

================================================
FILE: .github/workflows/docs.yml
================================================
name: docs
on:
  push:
    branches:
      - main
permissions:
  contents: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Configure Git Credentials
        run: |
          git config user.name github-actions[bot]
          git config user.email 41898282+github-actions[bot]@users.noreply.github.com
      - uses: actions/setup-python@v5
        with:
          python-version: 3.x
      - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV 
      - uses: actions/cache@v4
        with:
          key: mkdocs-material-${{ env.cache_id }}
          path: .cache
          restore-keys: |
            mkdocs-material-
      - run: pip install mkdocs-material 
      - run: mkdocs gh-deploy --force

================================================
FILE: .github/workflows/test.yml
================================================
name: Continuous Integration

on:
    pull_request:
    push:
      branches:
        - "main"

env:
    PYTEST_ADDOPTS: "--color=yes"

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - name: Install a specific version of uv
        uses: astral-sh/setup-uv@v5
        with:
          version: "0.6.3"
          enable-cache: true
      - run: uv python install  # Will read from .python-version
      - name: Install Dependencies
        run: uv sync --all-extras --dev
      - name: Run Tests
        run: |
            uv run make test-ci
      - name: Attach Code Coverage
        uses: py-cov-action/python-coverage-comment-action@v3.25
        with:
            GITHUB_TOKEN: ${{ github.token }}

      - name: Save Snapshot Report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: snapshot-report-posting
          path: snapshot_report.html
    
      - name: Store Pull Request Comment
        uses: actions/upload-artifact@v4
        if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true'
        with:
            # If you use a different name, update COMMENT_ARTIFACT_NAME accordingly
            name: python-coverage-comment-action
            # If you use a different name, update COMMENT_FILENAME accordingly
            path: python-coverage-comment-action.txt


================================================
FILE: .gitignore
================================================
# python generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# venv
.venv

# editor
.vscode/
.idea/
pyrightconfig.json

snapshot_report.html
*.afdesign
*.afdesign*
*.afphoto
*.afphoto*


================================================
FILE: .python-version
================================================
3.11.7


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Posting

Posting is open to contributions! 🚀

Contributions of any type, no matter the "size" and form, are welcome!
Bug reports, typo fixes, ideas, questions, and code are all valuable ways of contributing.
If you're new to open source and would like a bit of extra guidance, don't be afraid to ask - we all start somewhere 😌

## How do I contribute?

You can suggest ideas by creating a discussion, or report bugs by creating an issue.

If you wish to contribute code, and it's a change which is not an "objective improvement", please open a discussion first.
An "objective improvement" is a change which _indisputably_ improves Posting.
Some examples include adding test coverage, performance improvements, bug fixes, and fixing clear inconsistencies.

By opening a discussion, we can make sure:

- You're not working on something that someone else has already started.
- The feature is within Posting's scope.
- We can iron out the details before you go and commit a bunch of time to it!
- Maintainers can give you tips, and the discussion will be a place you can ask for help and guidance.

## Development

We use [uv](https://docs.astral.sh/uv/getting-started/installation/) to manage the development dependencies.

To setup an environment for development, run:

```bash
uv sync
```

This will create a virtual environment with the dependencies installed.
Activate the virtual environment with:

```bash
source .venv/bin/activate
# or, with fish shell:
source .venv/bin/activate.fish
```

Then you can run Posting with:

```bash
posting
```

If you wish to connect to the Textual dev tools (strongly recommended), you can run `posting` like this:

```bash
TEXTUAL=devtools,debug posting
```

The repo includes a test collection which is used in the automated tests, but you can also load it in for manual testing.

```bash 
TEXTUAL=devtools,debug posting --collection tests/sample-collections/ --env tests/sample-envs/sample_base.env --env tests/sample-envs/sample_extra.env
```

### Running the tests

To run the tests, you MUST use the `Makefile` commands.

```bash
make test
```

This will run the tests in parallel, but isolate a few tests which need to be run serially.
If you try to run the tests using a raw `pytest` command, you're gonna have a bad time.

### Snapshot testing

Snapshot testing is the primary way we test the UI of Posting.

It works by taking a "screenshot" of the running app at (actually an SVG!) the end of the test, and comparing it to the previous screenshot for that test.
If it matches, the test passes. If it doesn't, the test fails and you'll be able to view a report which shows the differences.

It's your job to look at this diff and consider whether the new output (on the left) is correct or not.

If the results are all as expected, you can update the snapshots by running:

```bash
make test-snapshot-update
```

This will update the snapshots saved on disk for all the tests which failed.
You should commit these changes into the repo - they're essentially the "source of truth" for what the UI of Posting should look like under different circumstances.

### Update the changelog

A changelog is maintained in the `docs/CHANGELOG.md` file, which follows the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.

When you're making a change which should be recorded in the changelog.
You should add your change to the `## Unreleased` section of the changelog.
If the `## Unreleased` section doesn't exist, you should add it at the top.

## Feeling unsure?

If you're feeling a bit stuck, just open a discussion and I'll do my best to help you out!


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================

.PHONY: test
test:
	$(run) pytest --cov=posting tests/ -n 24 -m "not serial" $(ARGS)
	$(run) pytest --cov-report term-missing --cov-append --cov=posting tests/ -m serial $(ARGS)

.PHONY: test-snapshot-update
test-snapshot-update:
	$(run) pytest --cov=posting tests/ -n 24 -m "not serial" --snapshot-update $(ARGS)
	$(run) pytest --cov-report term-missing --cov-append --cov=posting tests/ -m serial --snapshot-update $(ARGS)


.PHONY: test-ci
test-ci:
	$(run) pytest --cov=posting tests/ --cov-report term-missing $(ARGS)


================================================
FILE: NOTICE
================================================
This product includes software developed by [Encode OSS Ltd.](https://github.com/encode), under the BSD-3-Clause License:

BSD 3-Clause License

Copyright © 2019, Encode OSS Ltd. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

================================================
FILE: README.md
================================================
# Posting

**A powerful HTTP client that lives in your terminal.**

Posting is an HTTP client, not unlike Postman and Insomnia. As a TUI application, it can be used over SSH and enables efficient keyboard-centric workflows. Your requests are stored locally in simple YAML files, so they're easy to read and version control.

<img width="968" alt="image" src="https://github.com/user-attachments/assets/78359ab0-5e0c-4c0b-a60b-dce06b11bbf5" />

Some notable features include:

- "jump mode" navigation
- environments/variables
- autocompletion
- syntax highlighting using tree-sitter
- Vim keys
- customizable keybindings
- user-defined themes
- run Python code before and after requests
- extensive configuration
- open in $EDITOR/$PAGER
- import curl commands by pasting them into the URL bar
- export requests as cURL commands
- import from Postman and OpenAPI specs
- a command palette for quickly accessing functionality

Visit the [website](https://posting.sh) for more information, the roadmap, and the user guide.

## Installation

Posting can be installed via [uv](https://docs.astral.sh/uv/getting-started/installation/) on MacOS, Linux, and Windows.

```bash
# quickly install uv on MacOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# install Posting (will also quickly install Python 3.13 if needed)
uv tool install --python 3.13 posting
```

 Now you can run Posting via the command line:

```bash
posting
```

Homebrew and NixOS are not officially supported at the moment.

### Prefer `pipx`?

If you'd prefer to use `pipx`, that works too: `pipx install posting`.

## Learn More

Learn more about Posting at [https://posting.sh](https://posting.sh).

Posting was built with [Textual](https://github.com/textualize/textual).


================================================
FILE: docs/CHANGELOG.md
================================================
## 2.9.2 [14th October 2025]

### Fixed

- Fixed path parameters interference across requests.

## 2.9.1 [24th September 2025]

### Fixed

- Fixed unexpectedly high CPU usage via updating textual-autocomplete to 4.0.6.

## 2.9.0 [17th September 2025]

### Added

- Path parameter support (`:param` syntax) [(#295)](https://github.com/darrenburns/posting/pull/295)
- New built-in themes: `hypernova`, `synthwave`. [(#300)](https://github.com/darrenburns/posting/pull/300)
- Added `export: copy as YAML` command to the command palette [(#299)](https://github.com/darrenburns/posting/pull/299)

## 2.8.0 [13th September 2025]

### Changed

- Update to Textual 6.1.0 from 3.0.0.
- Remove notification when creating + opening a new request.

## 2.7.1 [22nd July 2025]

### Fixed

- Pin tree-sitter to <0.25.0 to fix crash due to breaking API change.

## 2.7.0 [19th April 2025]

### Added

- Collections can now be imported from Postman [(#106)](https://github.com/darrenburns/posting/pull/106)
- `posting.env` files in the current directory will automatically be loaded if no `--env` options are provided [(#249)](https://github.com/darrenburns/posting/pull/249)
- Generate JSON body with default values after importing OpenAPI specs [(#247)](https://github.com/darrenburns/posting/pull/247)
- Use tags to separate sub-collections in OpenAPI specs [(#247)](https://github.com/darrenburns/posting/pull/247)
- curl import now handles various different data options [(#252)](https://github.com/darrenburns/posting/pull/252)
- Added `-c` shorthand alias for `--collection` option [(#250)](https://github.com/darrenburns/posting/pull/250)
- Added `posting sponsors` command, which lists people who have supported Posting via GitHub Sponsors (specific sponsor tiers only). [(#253)](https://github.com/darrenburns/posting/pull/253)

### Changed

- A double-click rather than a single-click is now required to enter edit mode inside data tables (e.g. headers, query params, etc.) [(#256)](https://github.com/darrenburns/posting/pull/256)
- In the body editor, the `Form data` menu item now shows `(x-www-form-urlencoded)` after the label, to suggest the type of data being sent. [(#259)](https://github.com/darrenburns/posting/pull/259)

### Fixed

- Exported curl command uses `-d` for form data now, rather than `-F`. The result is exported commands with form data will now use `application/x-www-form-urlencoded` instead of `multipart/form-data` (matching Posting's behaviour). [(#252)](https://github.com/darrenburns/posting/pull/252)
- Fix attempting to add a protocol before applying variables in the URL bar [(#248)](https://github.com/darrenburns/posting/pull/248)
- Fix script path with custom function [(#254)](https://github.com/darrenburns/posting/pull/254)
- Fixed Posting's default `User-Agent` header not being used [(#259)](https://github.com/darrenburns/posting/pull/259)

## 2.6.0 [29th March 2025]

### Added

- Added `spacing: <compact|standard>` config to allow for a more compact UI (default: `standard`).
- Added ability to edit headers, form data, and query params without deleting and recreating them.
    - Press `enter` or click a row to enter edit mode. The row will be highlighted, and focus will move to the key/name input.
        Submitted changes will overwrite the existing row rather than adding a new one.
    - Press `v` to enter edit mode and immediately focus on the value input.
    - Press `escape` to cancel the edit.
    - Press `enter` to save the changes and exit edit mode.
    - The background colour behind the input will update to indicate edit mode is active.
- Added autocompletion for header values based on the header name.
    - For example, if the header is `Content-Type`, when typing the value, you'll be able to quickly autocomplete from a list of common content types.
- Press `/` to open the request search palette while the collection browser is focused.
    - `ctrl+shift+p` remains available as a global shortcut for this.
- Added `help: Open web docs` command to the command palette.
- Basic Vim motions to script output log (`hjkl`).
- Added response status code label to the URL bar.
- Contributing guide added to the GitHub repo (`CONTRIBUTING.md`).

### Changed

- Rewrite of the autocompletion system used to autocomplete header names, values, URLs, and variables.
- Automatically prepend `http://` protocol if no protocol is specified in the URL bar.
- Debounce jump overlay recomposition - if you resize while the jump overlay is open, it'll wait a short period before recomputing.
- Jump mode now lives on the main screen, rather than globally. This makes more sense as it's only available on the main screen. The only user-facing impact should be that the position of the keybinding in the footer and keybindings panel may change.
- Jump mode interaction with Tabs now uses the Tabs API, rather than simulating a Click.
- Status code label now displayed in the URL bar beside the trace markers.
- Request description area at the bottom of the collection browser has new design.
- When there are no response cookies, the cookies section will now display a message to the user.
- Updated to Textual 3.0.0.
- Various updates to the https://posting.sh homepage.

### Fixed

- Fixed scrolling in response headers and cookies tabs using keyboard.
- Fixed crash when immediately pressing enter after loading the UI when the `on_startup` config is set to `url` (this was due to lazy loading of the UI, and attempting to send a request before the UI was fully loaded).
- Fixed accepting a completion via enter in the header editor also adding the header.
    - Now, the first enter press will accept the completion, and the second enter press will add the header.
- Fixed Textual markup not being escaped key value tables.
- Fixed request description attempting to parse Textual markup [(#243)](https://github.com/darrenburns/posting/pull/243)

## 2.5.4 [13th March 2025]

### Fixed

- Prioritise user-defined `User-Agent` header over Posting's default.

## 2.5.3 [13th March 2025]

### Changed

- Lazily load content of tabs which are hidden on startup (100ms saved at startup).
- Only import openapi-pydantic when importing OpenAPI specs via `posting import` (63ms saved at startup).
- Pin httpx and patch out httpx._main to prevent slow import (20ms saved at startup).
- Defer import of watchfiles until app is running (6ms saved at startup).
- Defer `HelpScreen` import until it's used (10ms saved at startup).

## 2.5.2 [8th March 2025]

### Added

- Validation and corresponding UI feedback in New Request modal.

### Fixed

- Fixed crash when attempting to delete a request that doesn't exist on disk.
- Fixed being able to create requests with empty names.

## 2.5.1 [7th March 2025]

### Fixed

- Fixed importing of `max-time` from curl commands.

### Changed

- When importing a request from curl, request metadata (name, description) will not be overwritten.

## 2.5.0 [7th March 2025]

### Added

- Added bearer token auth support in the `Auth` tab.
- Added support for importing securitySchemes in OpenAPI specs.

### Changed

- OpenAPI specs are now parsed using an external library (`openapi-pydantic`).

## 2.4.1 [6th March 2025]

### Added

- Added command palette option to export the request as a curl command *without* running setup scripts.
- Added new documentation on working with the response in Posting and via external pagers/editors.
- Added new documentation on saving requests into folder structures from within Posting.

### Fixed

- Fixed crash when toggling rows via clicking the checkbox
- Fixed erroneous trailing ellipsis in `Info` tab.
- Fixed error messages in toasts on read and write timeouts.

### Changed

- Variables will be substituted into exported curl commands.
    - Undefined variables will be left as is (e.g. `$foo` will be left as `$foo` in the curl command)
- Setup scripts will now run by default when exporting to curl.
- Do not focus the URL bar when an error occurs on sending a request.
- Small visual refinement in `Scripts` tab.

## 2.4.0 [2nd March 2025]

### Added

- Added ability to toggle rows in tables on and off (press `space` or click the checkbox to toggle).
- Added "Export to curl" option in the command palette, to copy the request to your clipboard as a curl command.
- Added `curl_export_extra_args` config to allow for adding extra arguments to the curl command copied to your clipboard.
- Allow for customisation of "open in editor" and "open in pager" keys (`open-in-editor` and `open-in-pager` in the keymap).
- Added ability to quickly search for request by name and jump to it (press `ctrl+shift+p` to open the search popup).
- Added configurable keybinding `search-requests` (default: `ctrl+shift+p`).
- A few more screenshots were added to the "Navigation" guide.
- Added new headers to autocompletion: `Accept-Charset`, `DNT`, `Upgrade`, `Sec-Fetch-Site`, `Sec-Fetch-Mode`, `Sec-Fetch-User`, `Sec-Fetch-Dest`, and `Service-Worker-Navigation-Preload`.
- Removed some headers from autocompletion (due to being deprecated or response-only headers).

### Changed

- Upgraded Textual from version 0.86.0 to 2.1.1.

### Fixed

- Fixed variable preview not being shown below URL bar when cursor is over a variable.
- Fixed `ctrl+?` keybinding not opening contextual help on some terminals.

## 2.3.1 [1st March 2025]

### Changed

- Renamed "Change theme" to "Preview theme" in command palette, and update description to not imply the change persists across sessions (use the config file for persistent changes).

### Fixed

- Fixed crash when invalid syntax theme is specified. Posting now exits cleanly with an error message.
- Fixed toast message on copying text referring to "Response text" regardless of what text was copied.
- Fixed error handling and messaging when themes contain invalid syntax, invalid values. Includes batching errors and displaying multiple in one message.
- Fixed animation level config no longer being respected.
- Fixed missing `get_variable` method in scripting API that was described in docs but not implemented.

## 2.3.0 [19th November 2024]

### Added

- Editing a theme on disk will result in the UI refreshing in real-time to reflect changes.

## 2.2.0 [17th November 2024]

### Added

- Added 15 new themes (4 specific to Posting, 11 inherited from Textual's new theme system).
- Themes are now in submenu of command palette.
- Keybinding assistant can now be displayed as a sidebar, teaching you keybindings as you go.
- New tooltips when hovering over collection browser keybinds in the app footer.

### Changed 

- Syntax highlighting colours now derive automatically from the current theme.
- URL bar highlighting now derives automatically from the current theme.
- Method colour-coding in the collection browser is now derived automatically from the current theme.
- Jump mode UI has been refined to be more readable.
- Various refinements to existing themes.
- Options and descriptions in command palette reworded and reordered for clarity.
- Updated to Textual 0.86.1.

### Fixed

- Fixed error notification not rendering correctly when HTTP request times out.

## 2.1.1 [12th November 2024]

### Fixed

- Fix collection browser message not being visible when it's empty.

### Changed

- Improved message in empty collection browser, indicating keybind for how to toggle the collection browser.

## 2.1.0 [11th November 2024]

### Added

- Import curl command by pasting it into the URL bar.

### Changed

- Collection browser width now adjusts based on content, so it doesn't waste a lot of space on larger screens.
- Info tab now shows "Request not saved to disk" if a request has not been saved to disk, rather than "None" or nothing at all.

## 2.0.0 [18th October 2024]

### Added

- **Scripting**: Run Python scripts before and after sending requests. Scripts can be used to perform setup, set variables, modify requests, and more.
    - Define "setup", "pre-request" and "post-request" Python functions and attach them to requests.
    - Posting will automatically reload these functions when they change, meaning you can edit them in an external editor while Posting is running.
    - Scripts can be used to directly manipulate the request, set variables which are used in the request (e.g. set a `$token` variable which is used in the request URL).
    - Output from scripts is captured and displayed in the "Scripts" tab.
- **Keymaps**: Change the default keybindings for any of Posting's "global" actions (e.g. sending request, opening jump mode, etc.) by editing `keymap` section of your `config.yaml` file.
- Added `heading.hostname` config to allow customisation of the hostname in the header. This field supports Rich markup. You may wish to use this to apply highlighting when `posting` is running on a production system vs a development environment, for example.
- Added `focus.on_request_open` config to automatically shift focus when a request is opened via the collection browser. For example, you might prefer to have focus jump to the "Body" tab when a request is opened.
- More detail and screenshots added to several sections of the guide.
    - Much more detail added to the "Getting Started" section.
    - Collections guide updated to explain more about the collection browser.
    - Guide for Keymaps added.
    - Guide for Scripting added.
    - Guide for External Tools added (integrating with vim, less, fx, etc.)
- `alt`+`enter` can now be used to send a request (in addition to the existing `ctrl+j` binding).
- Tooltips added to more actions in the app footer. These appear on mouse hover.

### Changed

- Automatically apply `content-type` header based on the body type selected in the UI.
- Updated to Textual 0.83.0
- Various refinements to autocompletion, upgrading to textual-autocomplete 3.0.0a12.
- Dependency specifications loosened on several dependencies.
- Recommended installation method changed from rye to uv.

### Fixed

- Fixed double rendering in "jump mode" overlay.
- Fixed sidebar not working on mobile on https://posting.sh
- Fixed autocompletion appearing when on 1 item in the list and the "search string" is equal to that item.

## 1.13.0 [8th September 2024]

### Added

- New `collection_browser.show_on_startup` config to control whether the collection browser is shown on startup.
- Watch for changes to loaded dotenv files and reload UI elements that depend on them when they change.

### Changed

- Upgraded all dependencies.
- Remove `pydantic-settings` crash workaround on empty config files.
- Renaming `App.maximized` as it now clashes with a Textual concept.
- Removed "using default collection" message from startup.

### Fixed

- Fixed crash while rendering error message on timeout.

## 1.12.3 [4th September 2024]

### Fixed

- Fixed crash when no clipboard tool installed

## 1.12.2 [4th September 2024]

*This release was yanked. It's functionally identical to 1.12.1.*

## 1.12.1 [21st August 2024]

### Fixed

- Fix "invalid escape sequence" warnings on Python 3.12+
- Fix buttons in request deletion confirmation modal not being usable with the enter key.

## 1.12.0 [17th August 2024]

### Added

- Colour-coding of methods in the collection browser.
- Added FAQ to website.

## 1.11.0 [15th August 2024]

### Added

- This file, `CHANGELOG.md`.
- Launch docs website.
- Duplicate request (with new request popup) under cursor in tree with ++d++.
- "Quick" duplicate request (without new request popup, request name is auto-generated) under cursor in tree with ++shift+d++.
- Delete request (with confirmation modal) under cursor in tree with ++backspace++.
- "Quick" delete request (without confirmation modal) under cursor in tree with ++shift+backspace++.
- "Quit Posting" added to command palette.
- Move the sidebar to the right or left using `collection_browser.position: 'right' | 'left'` config.
- Refinements to "galaxy" theme.
- "galaxy" theme is now default.
- Help text added to "empty state" in the collection browser.
- Extend info in the "Collection Browser" help modal.
- Visual indicator (a red bar on the left) on Input fields that contain invalid values.
- Toast message now appears when trying to submit the 'new request' modal with invalid values.
- Public roadmap (initial brain-dump version).

### Fixed
- Ensure the location of the request on disk in the `Info` tab wraps instead of clipping out of view.
- Inserting requests in sorted position on creation.
- Prevent creating requests with no name.
- Prevent writing paths in the file-name field in the new request modal.
- Prevent specifying paths outside of the open collection dir in the directory field in the new request modal.
- Fix variables not being substituted into several fields, including auth.

### Changed

- Upgrade to Textual version 0.76.0
- Change logic to render bindings in help modal to reflect new Textual API.
- Sort order of requests in the tree improved.

---

!!! note
    Changes prior to 1.11.0 are not documented here.
    Please see the [Releases page](https://github.com/darrenburns/posting/releases) on GitHub for information on changes prior to 1.11.0.


================================================
FILE: docs/CNAME
================================================
POSTING.SH

================================================
FILE: docs/faq.md
================================================
# Frequently Asked Questions

## Using Posting

### How do I edit headers or query parameters?

Right now, you need to delete the row and re-create it with the correct values. Inline editing is planned, but not yet implemented.

## Contributing

### How do I suggest a feature?

You can suggest a feature by opening a Discussion on the [GitHub repository](https://github.com/darrenburns/posting/discussions) under the "Ideas" category.

### How do I report a bug?

You can report a bug by opening an Issue on the [GitHub repository](https://github.com/darrenburns/posting/issues).

### How do I contribute code to Posting?

You can contribute code to Posting by opening a Pull Request on the [GitHub repository](https://github.com/darrenburns/posting/pulls).

However, reporting bugs and suggesting features is also a great way to contribute!

A guide to contributing is coming soon.

## General

### How was Posting built?

Posting is built using [Textual](https://textual.textualize.io/), a Python framework for building terminal-based applications.

### Who is the original creator of Posting?

Posting was originally created by [Darren Burns](https://github.com/darrenburns), an open-source developer from Scotland, UK.

================================================
FILE: docs/guide/collections.md
================================================
## Overview

A *collection* is just a directory on your file system which may or may not contain requests in the `.posting.yaml` format.

There's absolutely nothing special about a collection.
It contains no "special files" or metadata -- it's just a directory.
It could even be empty.
"Collection" is simply the name we give to the directory which we've loaded into Posting.

## The collection browser

Posting displays the currently open collection in the sidebar.
This is called the *collection browser*.

![Posting collection tree](../assets/collection-tree.png){ height=300px }

The name of the currently open collection is displayed in the bottom right corner of the collection browser.
In the example above, the collection is named "sample-collection".

You can navigate this sidebar using the keyboard or mouse.
Open a request by clicking on it or pressing ++enter++ while it has focus,
and it'll be loaded into the main body of the UI.
A marker will also appear to the left of the request's title, indicating that the request is open.
A save operation will overwrite the currently open request.

!!! example "Keyboard shortcuts"

    The collection browser supports various keyboard shortcuts for quick navigation. For example ++shift+j++ and ++shift+k++ can be used to jump through sub-collections.
    Press ++f1++ while the browser has focus to view the full list of shortcuts.


The collection browser can be moved to the left or right side of the screen by setting the `collection_browser.position` configuration option
to either `"left"` or `"right"`.

## The default collection

If you launch Posting without a `--collection` argument, it will load the *default collection*, which is stored in Posting's reserved data directory on your file system.

The default collection can be thought of as a *system wide collection*.
It's a place to keep useful requests that you can easily access from anywhere, without having to manually specify a `--collection` argument.

You can check where this is by running `posting locate collection`.
The default collection is named "default", that name will be displayed in the bottom right corner of the collection browser.

![Posting default collection](../assets/default-collection.png)

This is useful to get started quickly, but you'll probably want to create your own collection directory and load it instead.
This makes it easier to organize your requests and check them into version control.

## Creating a collection

A collection is just a directory, so you can create a collection by simply creating an empty directory anywhere on your file system.

With the directory created, it's time to load it into Posting...

## Loading a collection

If you want to load a collection, you can do so by passing the path to the collection directory to Posting:

```bash
posting --collection path/to/collection
```

### Example

To open a collection (a directory containing requests), use the `--collection` option:

```bash
posting --collection path/to/collection
```

This will recursively find and display requests in the sidebar.
If you don't supply a directory, Posting will use the default collection directory.
You can check where the default collection is by running `posting locate collection`.

================================================
FILE: docs/guide/command_palette.md
================================================
## Overview

The *command palette* is a way to search for and execute commands in Posting.

Some functionality in Posting can only be accessed through the command palette.

It can be used to switch themes, show/hide parts of the UI, and more.

### Using the command palette

Press ++ctrl+p++ to open the command palette.


================================================
FILE: docs/guide/configuration.md
================================================
## Overview

Posting can be configured using a configuration file, environment variables, and/or `.env` files.

Configuration values are loaded in the following order of precedence (highest to lowest):

1. Configuration file
2. Environment variables
3. `.env` files

## Configuration file

You can write configuration for Posting using YAML.

The location of the config file can be checked using the command `posting locate config`.

Here's an example configuration file:

```yaml
theme: galaxy
layout: horizontal
response:
  prettify_json: false
heading:
  visible: true
  show_host: false
```

## Environment variables

All configuration values can also be set as environment variables.

Simply prefix the name of the config with `POSTING_` and set it as an environment variable.

For nested configuration values, use `__` as the delimiter. So to set `heading.visible` to `false`, you can set the environment variable `POSTING_HEADING__VISIBLE=false`.

For example, to set the theme to `galaxy`, you can set the environment variable `POSTING_THEME=galaxy`.

### dotenv (`.env`) files

Posting also supports `.env` (dotenv) files, which are useful if you want to swap out environment variable values depending on the environment you're working in (for example, "dev" vs "prod").

You can tell Posting to use a `.env` file using the `--env` option.
This option can be supplied multiple times to load multiple `.env` files.

Here's an example `.env` file:

```bash
POSTING_THEME="cobalt"
POSTING_LAYOUT="vertical"
POSTING_HEADING__VISIBLE="false"
```

Dotenv files are separate from collections, although you may wish to include them inside a collection to make it easy to version and share with others.

## Configuring SSL

Posting verifies SSL certificates by default using the CA bundle provided by the `certifi` package.

### SSL certificate configuration

Posting can load custom CA bundles from a `.pem` file.

The easiest way to do this is in your `config.yaml` file:

```yaml
ssl:
  ca_bundle: 'absolute/path/to/certificate.pem'
```

### Environment-specific certificates

If the required CA bundle differs per environment, you can again use the principle that all configuration can be set as environment variables which can optionally be set and loaded using `--env` and `.env` files:

```bash
# dev.env
POSTING_SSL__CA_BUNDLE='/path/to/certificate.pem'
```

Now load the `dev.env` file when working in the `dev` environment to ensure the dev environment CA bundle is used:

```bash
posting --env dev.env
```

### Disabling SSL verification

SSL verification can be disabled on a per-request basis in the "Options" tab.

### Client-side certificates

You can specify local certificates to use as a client-side certificate:

```yaml
ssl:
  certificate_path: /path/to/certificate.pem
  key_file: /path/to/key.key  # optional
  password: '***********'  # optional password for key_file
```

## Full configuration reference

The table below lists all available configuration options and their environment variable equivalents, their default values, and descriptions.

| Config Key (Env Var) | Values (Default) | Description |
|----------------------|------------------|-------------|
| `theme` (`POSTING_THEME`) | See the list of themes in the command palette (Default: `"galaxy"`) | Sets the theme of the application. |
| `load_user_themes` (`POSTING_LOAD_USER_THEMES`) | `true`, `false` (Default: `true`) | If enabled, load user themes from the theme directory, allowing them to be specified in config and selected via the command palette. |
| `load_builtin_themes` (`POSTING_LOAD_BUILTIN_THEMES`) | `true`, `false` (Default: `true`) | If enabled, load builtin themes, allowing them to be specified in config and selected via the command palette. |
| `theme_directory` (`POSTING_THEME_DIRECTORY`) | (Default: `${XDG_DATA_HOME}/posting/themes`) | The directory containing user themes. |
| `layout` (`POSTING_LAYOUT`) | `"vertical"`, `"horizontal"` (Default: `"horizontal"`) | Sets the layout of the application. |
| `use_host_environment` (`POSTING_USE_HOST_ENVIRONMENT`) | `true`, `false` (Default: `false`) | Allow/deny using environment variables from the host machine as variables in requests (using the standard `$` syntax). When disabled, only variables defined explicitly in `.env` files will be available for use. |
| `watch_env_files` (`POSTING_WATCH_ENV_FILES`) | `true`, `false` (Default: `true`) | If enabled, automatically reload environment files when they change. |
| `watch_themes` (`POSTING_WATCH_THEMES`) | `true`, `false` (Default: `true`) | If enabled, automatically reload themes in the theme directory when they change on disk. |
| `watch_collection_files` (`POSTING_WATCH_COLLECTION_FILES`) | `true`, `false` (Default: `true`) | If enabled, automatically reload collection files when they change on disk. Right now, this is limited to reloading Python scripts in the collection. |
| `animation` (`POSTING_ANIMATION`) | `"none"`, `"basic"`, `"full"` (Default: `"none"`) | Controls the animation level. |
| `response.prettify_json` (`POSTING_RESPONSE__PRETTIFY_JSON`) | `true`, `false` (Default: `true`) | If enabled, JSON responses will be pretty-formatted. |
| `response.show_size_and_time` (`POSTING_RESPONSE__SHOW_SIZE_AND_TIME`) | `true`, `false` (Default: `true`) | If enabled, the size and time taken for the response will be displayed in the response area border subtitle. |
| `heading.visible` (`POSTING_HEADING__VISIBLE`) | `true`, `false` (Default: `true`) | Show/hide the app header. |
| `heading.show_host` (`POSTING_HEADING__SHOW_HOST`) | `true`, `false` (Default: `true`) | Show/hide the hostname in the app header. |
| `heading.show_version` (`POSTING_HEADING__SHOW_VERSION`) | `true`, `false` (Default: `true`) | Show/hide the version in the app header. |
| `heading.hostname` (`POSTING_HEADING__HOSTNAME`) | (Default: `unset`) | The hostname to display in the app header. You may use Rich markup here. If unset, the hostname provided via `socket.gethostname()` will be used. |
| `url_bar.show_value_preview` (`POSTING_URL_BAR__SHOW_VALUE_PREVIEW`) | `true`, `false` (Default: `true`) | Show/hide the variable value preview below the URL bar. |
| `url_bar.hide_secrets_in_value_preview` (`POSTING_URL_BAR__HIDE_SECRETS_IN_VALUE_PREVIEW`) | `true`, `false` (Default: `true`) | If enabled, values will be redacted in the value preview when the variable name contains the word `secret` or `key` or `password` or `token`. |
| `collection_browser.position` (`POSTING_COLLECTION_BROWSER__POSITION`) | `"left"`, `"right"` (Default: `"left"`) | The position of the collection browser on screen. |
| `collection_browser.show_on_startup` (`POSTING_COLLECTION_BROWSER__SHOW_ON_STARTUP`) | `true`, `false` (Default: `true`) | Show/hide the collection browser on startup. Can always be toggled using the command palette. |
| `pager` (`POSTING_PAGER`) | (Default: `$PAGER`) | Command to use for paging text. |
| `pager_json` (`POSTING_PAGER_JSON`) | (Default: `$PAGER`) | Command to use for paging JSON. |
| `editor` (`POSTING_EDITOR`) | (Default: `$EDITOR`) | Command to use for opening files in an external editor. |
| `ssl.ca_bundle` (`POSTING_SSL__CA_BUNDLE`) | Absolute path (Default: `unset`) | Absolute path to a CA bundle file/dir. If not set, the [Certifi](https://pypi.org/project/certifi/) CA bundle will be used. |
| `ssl.certificate_path` (`POSTING_SSL__CERTIFICATE_PATH`) | Absolute path (Default: `unset`) | Absolute path to a client SSL certificate file or directory. |
| `ssl.key_file` (`POSTING_SSL__KEY_FILE`) | Absolute path (Default: `unset`) | Absolute path to a client SSL key file. |
| `ssl.password` (`POSTING_SSL__PASSWORD`) | Password for the key file. (Default: `unset`) | Password to decrypt the key file if it's encrypted. |
| `focus.on_startup` (`POSTING_FOCUS__ON_STARTUP`) | `"url"`, `"method", "collection"` (Default: `"url"`) | Automatically focus the URL bar, method, or collection browser when the app starts. |
| `focus.on_response` (`POSTING_FOCUS__ON_RESPONSE`) | `"body"`, `"tabs"` (Default: `unset`)| Automatically focus the response tabs or response body text area when a response is received. |
| `focus.on_request_open` (`POSTING_FOCUS__ON_REQUEST_OPEN`) | `"headers"`, `"body"`, `"query"`, `"info"`, `"url"`, `"method"` (Default: `unset`) | Automatically focus the specified target when a request is opened from the collection browser. |
| `text_input.blinking_cursor` (`POSTING_TEXT_INPUT__BLINKING_CURSOR`) | `true`, `false` (Default: `true`) | If enabled, the cursor will blink in input widgets and text area widgets. |
| `command_palette.theme_preview` (`POSTING_COMMAND_PALETTE__THEME_PREVIEW`) | `true`, `false` (Default: `false`) | If enabled, the command palette will display a preview of the selected theme when the cursor is over it. This will slow down cursor movement and so is disabled by default. |
| `use_xresources` (`POSTING_USE_XRESOURCES`) | `true`, `false` (Default: `false`) | Try to create themes called `xresources-dark` and `xresources-light` (see the section below) |
| `curl_export_extra_args` (`POSTING_CURL_EXPORT_EXTRA_ARGS`) | (Default: `""`) | Extra arguments to pass to curl when exporting a request as a curl command. This string will be inserted directly into the command that gets copied to your clipboard, immediately after `curl `. |

================================================
FILE: docs/guide/environments.md
================================================
## Overview

You can use *variables* in input fields and text areas using the `${VARIABLE_NAME}` or `$VARIABLE_NAME` syntax.
These variables will be substituted into outgoing requests.

<p align="center">
  <img src="https://github.com/darrenburns/posting/assets/5740731/24b64f58-747b-409e-9672-e354eb8994d8" alt="url-bar-environments-short">
</p>

## Loading variables

Variables are stored in `.env` files, and loaded using the `--env` option.

Here's what a `.env` file might look like:

```bash
# file: dev.env
API_KEY="dev-api-key"
ENV_NAME="dev"
BASE_URL="https://${ENV_NAME}.example.com"
```

To make these variables available in the UI, you can load them using the `--env` option:

```bash
posting --env dev.env
```

You can load multiple `.env` files by specifying the `--env` option multiple times:

```bash
posting --env dev.env --env shared.env
```

This allows you to build up a set of variables which are common to all environments, and then override them for specific environments.

## Autoloading `.env` files

If no `--env` options are provided, Posting will automatically load a `posting.env` file in the current working directory if it exists.

## Using environment variables

By default, Posting will only use variables defined in `.env` files that have been explicitly loaded using the `--env` option.

If you want to permit using environment variables that exist on the host machine (i.e. those which are not defined in any `.env` files), you must set the `use_host_environment` config option to `true` (or set the environment variable `POSTING_USE_HOST_ENVIRONMENT=true`).

## Practical example

Imagine you're testing an API which exists in both `dev` and `prod` environments.

The `dev` and `prod` environments share some common variables, but differ in many ways too.
We can model this by having a single `shared.env` file which contains variables which are shared between environments, and then a `dev.env` and `prod.env` file which contain environment specific variables.

```bash
# file: shared.env
API_PATH="/api/v1"
ENV_NAME="shared"

# file: dev.env
API_KEY="dev-api-key"
ENV_NAME="dev"
BASE_URL="https://${ENV_NAME}.example.com"

# file: prod.env
API_KEY="prod-api-key"
ENV_NAME="prod"
BASE_URL="https://${ENV_NAME}.example.com"
```

When working in the `dev` environment, you can then load all of the shared variables and all of the development environment specific variables using the `--env` option:

```bash
posting --env shared.env --env dev.env
```

This will load all of the shared variables from `shared.env`, and then load the variables from `dev.env`. Since `ENV_NAME` appears in both files, the value from the `dev.env` file will be used since that was the last one specified.

Note that you do *not* need to restart to load changes made to these files,
so you can open and edit your env files in an editor of your choice alongside Posting.

### Environment specific config

Since all Posting configuration options can also be specified as environment variables, we can also put environment specific config inside `.env` files. There's a dedicated "Configuration" section in this document which covers this in more detail.

For example, if you wanted to use a light theme in the prod environment (as a subtle reminder that you're in production!), you could set the environment variable `POSTING_THEME=solarized-light` inside the `prod.env` file.

Note that configuration files take precedence over environment variables, so if you set a value in both a `.env` file and a `config.yaml`, the value from the `config.yaml` file will be used.


================================================
FILE: docs/guide/external_tools.md
================================================
## Overview

You can quickly switch between Posting and external editors and pagers.

For example, you could edit request bodies in `vim`, and then browse the JSON response body in `less` or `fx`.

You can even configure a custom pager specifically for browsing JSON.

## External Editors

With a multi-line text area focused, press ++f4++ to open the file in your
configured external editor.

The configured external editor can be set as `editor` in your `config.yaml`
file.
For example:

```yaml title="config.yaml"
editor: vim
```

Alternatively, you can set the `POSTING_EDITOR` environment variable.

```bash
export POSTING_EDITOR=vim
```

If neither is set, Posting will try to use the `EDITOR` environment variable.

!!! tip "Using VSCode or Cursor"

    If you want to use VSCode or Cursor, you can set the `POSTING_EDITOR` environment variable to `code -w` or `cursor -w` respectively.

## External Pagers

With a multi-line text area focused, press ++f3++ to open the file in your
configured external pager.

The configured external pager can be set as `pager` in your `config.yaml`
file.
For example:

```yaml title="config.yaml"
pager: less
```

Alternatively, you can set the `POSTING_PAGER` environment variable.

```bash
export POSTING_PAGER=less
```

### JSON Pager

You can use a custom pager for viewing JSON using the `pager_json` setting in
your `config.yaml` file.
For example:

```yaml title="config.yaml"
pager_json: fx
```

Alternatively, you can set the `POSTING_PAGER_JSON` environment variable.

```bash
export POSTING_PAGER_JSON=fx
```

If neither is set, Posting will try to use the default pager lookup rules discussed earlier.

## Exporting to curl

> *Added in Posting 2.4.0*

Open the command palette and select `export: copy as curl`.
This will transform the open request into a cURL command, and copy it to your clipboard.

![Screenshot of command palette with the export: copy as curl option](../assets/curl-export.png)

You can optionally supply extra arguments to pass to curl by setting the `curl_export_extra_args` setting in your `config.yaml` file.

```yaml title="config.yaml"
curl_export_extra_args: "--verbose -w %{time_total} %{http_code}"
```

This will be inserted directly into the command that gets copied to your clipboard, immediately after `curl `,
producing a command like the following:

```bash
curl --verbose -w %{time_total} %{http_code} -X POST ...
```



================================================
FILE: docs/guide/help_system.md
================================================
## Overview

Posting has a *built-in help system*, which can be used to get information about the currently focused widget.

### Getting help for the focused widget

With a widget focused, press `f1` to open a help window for that widget.

<img width="1229" alt="image" src="https://github.com/user-attachments/assets/707be55f-6dfc-4faf-b9f3-fe7bc5422008">

Most widgets offer more keybindings and functionality than meets the eye, and more than what is shown in the application footer.

The help window explains how to use the focused widget, and lists all of the keybindings offered by it.

================================================
FILE: docs/guide/importing.md
================================================
## Overview

Posting supports importing from external sources.

## Importing from curl

!!! example "This feature is experimental."

You can import a curl command by pasting it into the URL bar.

This will fill out the request details in the UI based on the curl command you pasted, overwriting any existing values.

## Importing from OpenAPI

!!! example "This feature is experimental."

Posting can convert OpenAPI 3.x specs into collections.

To import an OpenAPI Specification, use the `posting import path/to/openapi.yaml` command.

You can optionally supply an output directory.

If no output directory is supplied, the default collection directory will be used.

Posting will attempt to build a file structure in the collection that aligns with the URL structure of the imported API.

## Importing from Postman

!!! example "This feature is experimental."

Collections can be imported from Postman.

To import a Postman collection, use the `posting import --type postman path/to/postman_collection.json` command.

You can optionally supply an output directory with the `-o` option.
If no output directory is supplied, the default collection directory will be used (check where this is using `posting locate collection`).

Variables will also be imported from the Postman collection and placed in a `.env` file inside the collection directory.


================================================
FILE: docs/guide/index.md
================================================
Posting can be installed in a matter of seconds on MacOS, Linux, and Windows.

## Installation

The recommended method is to use [uv](https://docs.astral.sh/uv/getting-started/installation/), which is a single Rust binary that you can use to install Python apps.
It's significantly faster than alternative tools, and will get you up and running with Posting in seconds.

You don't even need to worry about installing Python yourself - `uv` will manage everything for you.

### uv

Here's how to install Posting using `uv`:

```bash
# quick install on MacOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# install Posting (will also quickly install Python 3.12 if needed)
uv tool install --python 3.12 posting

# Run posting
posting
```

`uv` can also be installed via Homebrew, Cargo, Winget, pipx, and more. See the [installation guide](https://docs.astral.sh/uv/getting-started/installation/) for more information.

`uv` also makes it easy to install additional Python packages into your Posting environment, which you can then use in your pre-request/post-response scripts.

### pipx

If you prefer, you can install Posting via [`pipx`](https://pipx.pypa.io/stable/).

```bash
pipx install posting
```

---

The methods above will both install Posting globally, in an isolated environment. Do *not* attempt to install Posting with `pip`.

??? failure "Homebrew is not supported"

    Installing via Homebrew is not supported, as some of Posting's Rust and C dependencies can take over 10 minutes to compile. When using uv, installation time is measured in milliseconds, and with pipx it's just a few seconds.

<!-- 
On MacOS, you can also install Posting via Homebrew:

```bash
brew install darrenburns/homebrew/posting
```

Note that the Homebrew installation method requires compiling some Rust dependencies, and may take a few minutes to complete. -->

## Choose your preferred UI style

Posting comes with two different "spacing" modes: `compact` and `standard`.

The default is `standard` which includes extra padding and borders around content.
`compact` mode removes the padding and borders, and uses a more compact layout.

You can toggle the spacing mode using the `spacing` command from the command palette (++ctrl+p++ then type `spacing`, then press ++enter++).
From this command palette, you can also try out different themes.

<video controls src="../assets/themes-and-compact-mode-23mar25-c.mp4" title="Themes and compact mode" autoplay loop muted playsinline></video>

To permanently change the spacing mode and/or theme, open the config file (`posting locate config` will tell you where it is) and add the following line(s):

```yaml
spacing: compact
theme: <theme-name>
```

## A quick introduction

This introduction will show you how to create a simple POST request to the [JSONPlaceholder](https://jsonplaceholder.typicode.com/) mock API to create a new user. It focuses on an efficient keyboard-driven workflow, but you can also use the mouse if you prefer.

### Collections and requests

A *collection* is simply a directory which may contain requests saved by Posting.

If you launch Posting without specifying a collection, any requests you create will be saved to the `"default"` collection.

The default collection is a directory reserved by Posting on your filesystem. It's a "global" collection and is not related to the directory you launched Posting from.

This is fine for quick throwaway requests, but you'll probably want to create a new collection for each project you work on so that you can check it into version control.

To create a new collection, simply create a new directory and pass it into Posting.

```bash
mkdir my-collection
posting --collection my-collection
```

Now, any requests you create will be saved in the `my-collection` directory as simple YAML files with the `.posting.yaml` extension.

When Posting opens, you'll see the collection browser on the left side of the screen with `my-collection` displayed at the bottom right corner.

### Setting the request method to POST

When you launch Posting, no request is open, so the UI will look rather empty.

Let's create a simple POST request to the [JSONPlaceholder](https://jsonplaceholder.typicode.com/) mock API to create a new user.

Press ++ctrl+t++ to open the request method dropdown:

![Posting request method dropdown](../assets/request-method-dropdown.png)

The underlined character in each method indicates the key you can press to quickly select that method. We want to send a POST request, so press ++p++ to quickly select the POST method.

### Setting the request URL

You can move focus forward and backward through widgets using ++tab++ and ++shift+tab++ respectively.
So, you can move focus from the method selector to the URL bar by pressing ++tab++ once.

Alternatively, you can immediately move the focus to the URL bar from anywhere in Posting using ++ctrl+l++.

Type `https://jsonplaceholder.typicode.com/users` into the URL bar.

![Posting URL bar](../assets/url-bar.png)

Posting's URL bar highlights parts of the URL as you type, which can be helpful for spotting typos.

It can also autocomplete domains you've previously used, to save you from having to retype them.
For example, if you later want to make a request to `https://jsonplaceholder.typicode.com/posts`, you can simply type "json" into the URL bar and select the URL from the autocomplete menu that appears.

![Posting URL autocomplete](../assets/url-autocomplete.gif)

### Adding a JSON body

Press ++ctrl+o++ to enter "jump mode", then press ++w++ to quickly jump to the "Body" tab.
Jump mode is great for quickly moving through the UI without having to press ++tab++ multiple times.

At this point focus is currently on the tab bar itself.
Press ++j++ (or ++down++) to move the cursor down to the dropdown.
Press ++enter++ to open it, then select the option `Raw (json, text, etc.)`.

Move down to the text area below using ++j++ (or ++down++), and type (or paste) the JSON below. 

```json
{
  "name": "John Doe",
  "username": "johndoe",
  "email": "john.doe@example.com"
}
```

Note at the bottom right of the text area, JSON is pre-selected as the content type.
This means Posting will automatically use JSON syntax highlighting and it will insert the `Content-Type: application/json` header for you when the request is sent.

### Viewing keyboard shortcuts

Now is probably a good time to note that you can see the full list of keyboard shortcuts for the focused widget by pressing ++f1++. The text area widget in particular has a lot of useful shortcuts and supports things like undo/redo.

!!! tip "Changing keyboard shortcuts"

    You can remap keybindings in Posting using [Keymaps](../guide/keymap.md).

### Sending the request

Press ++ctrl+j++ to send the request.
This shortcut works globally.

!!! tip "Keyboard shortcuts"

    You may also be able to send the request using ++alt+enter++.
    This only works on terminals that support the Kitty keyboard protocol.


### Working with the response

The response will be displayed in the main body of the UI.
Press ++ctrl+o++ to enter "jump mode", and the ++a++ to move to the response `Body` tab.

Press ++j++ or ++down++ to move the cursor down into the response body.
This text area supports a bunch of different keyboard shortcuts for quickly navigating the response body.
Text can be selected by holding ++shift++ and moving the cursor using the arrow keys (or `hjkl` keys for Vim fans).
You can also select text by clicking and dragging with the mouse.

Press ++y++ or ++c++ to copy the selected text to your clipboard.
If no text is selected, the entire response body will be copied.

!!! tip "Vim keys"

    The response text area supports some Vim-inspired keyboard shortcuts.

    - To select text without holding ++shift++, you can press ++v++ to enter visual mode, and use `hjkl` to navigate.
    - If your cursor is at a bracket, you can press ++%++ to jump to the matching bracket.
    - Press ++w++ to move the cursor to the next word, and ++b++ to move the cursor to the previous word.

    Try experimenting to find out what's supported, and if you're desperately missing something, please start a discussion on [GitHub Discussions](https://github.com/darrenburns/posting/discussions).

You can open the response using the command defined in your `$EDITOR`, `$POSTING_EDITOR`, `$POSTING_PAGER`, or `$POSTING_PAGER_JSON` environment variables.
For example, if you set `$POSTING_PAGER_JSON` to `fx`, then press the corresponding keybind to open the pager when the response text area has focus, the response will be opened in the `fx` JSON viewer.

### Saving the request

Finally, press ++ctrl+s++ to save the request to disk.
Fill out the form on the modal that appears, and press ++enter++ or ++ctrl+n++ to write the request to disk.

!!! tip "Folders"

    Requests can be saved to folders - simply include a `/` in the `Path in collection` field when you save the request,
    and Posting will create the required directory structure for you.


================================================
FILE: docs/guide/keymap.md
================================================
## Overview

As explained in the [Help System](./help_system.md) section, you can view the keybindings for any widget by pressing ++f1++ or ++ctrl+question-mark++ when that widget has focus.

If you wish to use different keybindings, you can do so by editing the `keymap` section of your `config.yaml` file.
Check the location of that file on your system by running `posting locate config` on the command line.

### Changing the keymap

Actions in Posting have unique IDs which map to a keybinding (listed at the bottom of this page).
For any of these IDs, you can change the keybinding by adding an entry to the `keymap` section of your `config.yaml` file:

```yaml
keymap:
  <ID>: <key-combination>
```

Here's an example of changing the keybinding for the "Send Request" action:

```yaml
keymap:
  send-request: ctrl+r
```

After adding the above entry to `config.yaml` and restarting Posting, you'll notice that that the footer of the app now shows `^r` to send a request rather than the default `^j`.

Now you can press `^r` to send a request *instead of* `^j`.

You can also have multiple keys map to the same action by separating them with commas:

```yaml
keymap:
  send-request: ctrl+r,ctrl+i
```

Note that by adding an entry to the `keymap` you are overriding the default keybinding for that action, so if you wish to keep the default keybinding, you'll need to specify it again:

```yaml
keymap:
  send-request: ctrl+r,ctrl+i,ctrl+j
```

### Key format

Support for keys in the terminal varies between terminals, multiplexers and operating systems.
It's a complex topic, and one that may involve some trial and error.
Some keys might be intercepted before reaching Posting, and your emulator might not support certain keys.

- To specify ++ctrl+x++, use `ctrl+x`.
- To specify ++ctrl+shift+x++, use `ctrl+X` (control plus uppercase "X").
- To specify multiple keys, separate them with commas: `ctrl+shift+left,ctrl+y`.
- To specify a function key, use `f<number>`. For example, ++f1++ would be `f1`.
- To specify `@` (at) use `at` (*not* e.g. ++shift+2++ as this only applies to some keyboard layouts).
- Arrow keys can be specified as `left`, `right`, `up` and `down`.
- `shift` works as a modifier non-printable keys e.g. `shift+backspace`, `shift+enter`, `shift+right` are all acceptable. Support may vary depending on your emulator.
- `alt` also works as a modifier e.g. `alt+enter`.
- `ctrl+enter`, `alt+enter`,`ctrl+backspace`, `ctrl+shift+enter`, `ctrl+shift+space` etc. are supported if your terminal supports the Kitty keyboard protocol.
- Other keys include (but are not limited to) `comma`, `full_stop`, `colon`, `semicolon`, `quotation_mark`, `apostrophe`, `left_bracket`, `right_square_bracket`, `left_square_bracket`, `backslash`, `vertical_line` (pipe |), `plus`, `minus`, `equals_sign`, `slash`, `asterisk`,`tilde`, `percent_sign`.

The only way to know for sure which keys are supported in your particular terminal emulator is to install Textual, run `textual keys`, press the key you want to use, and look at the `key` field of the printed output.

!!! example "Work in progress"
    In the future, I hope to make it easier to discover which keys are supported and when key presses they correspond to for a particular environment directly within Posting. This will likely take the form of a CLI command that outputs key names and their corresponding key presses. For now, if you need assistance, please open a discussion on [GitHub](https://github.com/darrenburns/posting/discussions).

### Binding IDs

These are the IDs of the actions that you can change the keybinding for:

- `send-request` - Send the current request. Default: `ctrl+j,alt+enter`.
- `focus-method` - Focus the method selector. Default: `ctrl+t`.
- `focus-url` - Focus the URL input. Default: `ctrl+l`.
- `save-request` - Save the current request. Default: `ctrl+s`.
- `expand-section` - Expand or shrink the section which has focus. Default: `ctrl+m`.
- `toggle-collection` - Toggle the collection browser. Default: `ctrl+h`.
- `new-request` - Create a new request. Default: `ctrl+n`.
- `commands` - Open the command palette. Default: `ctrl+p`.
- `help` - Open the help dialog for the currently focused widget. Default: `f1,ctrl+question_mark`.
- `quit` - Quit the application. Default: `ctrl+c`.
- `jump` - Enter jump mode. Default: `ctrl+o`.
- `open-in-pager` - Open the content of the focused text area in your $PAGER/$POSTING_PAGER/$POSTING_PAGER_JSON. Default: `f3`.
- `open-in-editor` - Open the content of the focused text area in your $EDITOR/$POSTING_EDITOR. Default: `f4`.
- `search-requests` - Go to a request by name. Default: `ctrl+shift+p`.


================================================
FILE: docs/guide/navigation.md
================================================
Posting can be navigated using either mouse or keyboard.

## Jump mode

Jump mode is the fastest way to get around.

Press <kbd>ctrl</kbd>+<kbd>o</kbd> to enter jump mode, followed by the key corresponding to the widget you want to switch focus to (jump to).

![Jump mode preview image](../assets/jump-mode.png)

With the default layout, the positioning of keys on the overlays is similar to the positioning of the keys on a QWERTY keyboard.

To exit jump mode, press <kbd>esc</kbd>.

## Tab navigation

<kbd>tab</kbd> and <kbd>shift+tab</kbd> will move focus between widgets,
and <kbd>j</kbd>/<kbd>k</kbd>/<kbd>up</kbd>/<kbd>down</kbd> will move around within a widget.

Some widgets have additional keybindings for navigation.
You can check these by pressing <kbd>f1</kbd> while it is focused.

Where it makes sense, <kbd>up</kbd> and <kbd>down</kbd> will also move between widgets.

## Mouse navigation

You can also navigate Posting entirely using the mouse, very much like a typical GUI application.

If a widget shows a scrollbar, you can use the mouse wheel or trackpad gestures to scroll through its content.
Scrollbars can also be clicked and dragged.

If you hold shift and scroll using the trackpad or mousewheel, the content will scroll horizontally (if there's a horizontal scrollbar).

## Searching and jumping to requests

Press <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>p</kbd> to open the fuzzy search popup (configurable using the `search-requests` keybinding, see [keymap](./keymap.md)).

Type the name of the request you want to jump to and press <kbd>enter</kbd> to open it.

![Search requests preview image](../assets/search-requests.png)

## Contextual help

Many widgets have additional bindings for navigation other than those displayed in the footer.
You can view the full list of keybindings for the currently focused widget, as well as additional usage information and tips, by pressing <kbd>f1</kbd> or <kbd>ctrl</kbd>+<kbd>?</kbd> (or <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>/</kbd>).

![Contextual help preview image](../assets/contextual-help.png)

## Automatic focus switching

You can use the `focus.on_startup` and `focus.on_response` configuration options to control which widget is focused when the app starts and when a response is received.

| Config | Default value | Description |
|----------------------|---------------|-------------|
| `focus.on_startup` | `"url"`, `"method", "collection"` (Default: `"url"`) | Automatically focus the URL bar, method, or collection browser when the app starts. |
| `focus.on_response` | `"body"`, `"tabs"` (Default: `unset`)| Automatically focus the response tabs or response body text area when a response is received. |
| `focus.on_request_open` | `"headers"`, `"body"`, `"query"`, `"info"`, `"url"`, `"method"` (Default: `unset`) | Automatically focus the specified target when a request is opened from the collection browser. |

## Exiting

Quit Posting by pressing <kbd>ctrl</kbd>+<kbd>c</kbd>, or by opening the command palette and selecting "Quit".

================================================
FILE: docs/guide/requests.md
================================================
## Overview

Requests are stored directly on your file system as simple YAML files, suffixed with `.posting.yaml` - easy to read, understand, and version control!

## Example

Here's an example of what a request file looks like:

```yaml
name: Create user
description: Adds a new user to the system.
method: POST
url: https://jsonplaceholder.typicode.com/users
body: 
  content: |-
    {
      "firstName": "John",
      "email": "john.doe@example.com"
    }
headers:
- name: Content-Type
  value: application/json
params:
- name: sendWelcomeEmail
  value: 'true'
```

## Creating a new request

Press ++ctrl+n++ to create a new request.

You'll be prompted to supply a name for the request.
By default, this name is used to generate the filename, but you can also choose your own filename if you wish.

!!! tip
    If you already have a collection loaded, the path in the "New Request" dialog will be pre-filled based on the position of the cursor in the collection tree, so moving the cursor to the correct location *before* pressing ++ctrl+n++ will save you from needing to type out the path.

Within the "Path in collection" field of this dialog, it's important to note that `.` refers to the currently loaded *collection* directory (that is, the directory that was loaded using the `--collection` option), and *not* necessarily the current working directory.

### Duplicating a request

With a the cursor over a request in the collection tree, press ++d++ to create a duplicate of that request. This will bring up a dialog allowing you to change the name and description of the request, or move it to another location.

To skip the dialog and quickly duplicate the request, press ++shift+d++, creating it as a sibling of the original request. The file name of the new request will be generated automatically. You can always modify the name and description after it's created in the `Info` tab.

## Saving a request

Press ++ctrl+s++ to save the currently open request.

If you haven't saved the request yet, a dialog will appear, prompting you to give the request a name, and to select a directory to save it in.

!!! tip "Folders"

    Requests can be saved to folders - simply include a `/` in the `Path in collection` field when you save the request, and Posting will create the required directory structure for you.

If the request is already saved on disk, ++ctrl+s++ will overwrite the previous version with your new changes.

## Loading requests

Requests are stored on your file system as simple YAML files, suffixed with `.posting.yaml`.

A directory can be loaded into Posting using the `--collection` option, and all `.posting.yaml` files in that directory will be displayed in the sidebar.

## Path parameters

Path parameters let you insert placeholders directly in the URL path using `:name` syntax. For example:

```
https://api.example.com/users/:id/comments/:commentId
```

When you type placeholders like this in the URL bar, Posting automatically extracts them and shows them in the Path tab. You can edit the values there, but you cannot add or remove rows manually — the rows come from the URL. Values are substituted into the URL path when the request is sent.

### Useful shortcuts

- With the cursor over a `:name` token in the URL bar, press ++alt+down++ to jump to that row in the Path tab.
- With a row highlighted in the Path tab, press ++alt+down++ to jump to the corresponding token in the URL bar.


### YAML representation

Path parameter values are saved in the request file under `path_params`:

```yaml
name: get comments
url: https://jsonplaceholder.typicode.com/posts/:postId/comments
path_params:
- name: postId
  value: '3'
```

You can also use variables from the environment in path parameter values (e.g., `$FOO`). Variables are resolved before the values are substituted into the URL.

### Escaping literal colons

If you need a literal `:name` in the path, escape it by doubling the colon. For example, `::id` renders as `:id` and is not treated as a placeholder:

```
http://example.com/users/::id/:id  →  http://example.com/users/:id/123
```

## Deleting a request

You can delete a request by moving the cursor over it in the tree, and pressing ++backspace++.

## Sharing requests

An easy way to share a request with others is to copy it as a cURL command.
Press ++ctrl+p++ and select `export: copy as curl` to copy the request as a cURL command to your clipboard.

You can also press ++ctrl+p++ and select `export: copy as YAML` to copy the request as YAML. This provides a quick way to share a request with other Posting users, e.g. via Slack.


================================================
FILE: docs/guide/scripting.md
================================================
## Overview

You can attach simple Python scripts to requests inside the `Scripts` tab, and have Posting run them at various stages of the request lifecycle. This powerful feature allows you to:

- Perform setup before a request (e.g. setting variables, preparing data)
- Set or modify headers, query parameters, and other request properties
- Print logs and messages
- Set variables to be used in later requests (e.g. authentication tokens)
- Inspect request and response objects, and manipulate them
- Pretty much anything else you can think of doing with Python!

<img src="../../assets/scripts-tab.png" alt="Scripts tab" />

## Script types

Posting supports three types of scripts, which run at different points in the request/response lifecycle:

1. **Setup Scripts**: Runs before the request is constructed. This is useful for setting initial variables which may be substituted into the request.
2. **Pre-request Scripts**: Runs after the request has been constructed and variables have been substituted, but before the request is sent. You can directly modify the request object here.
3. **Post-response Scripts**: Runs after the response is received. This is useful for extracting data from the response, or for performing cleanup.

## Writing scripts

In the context of Posting, a "script" is a regular Python function.

By default, if you specify a path to a Python file, Posting will look for and execute the following functions at the appropriate times:

- `setup(posting: Posting) -> None`
- `on_request(request: RequestModel, posting: Posting) -> None`
- `on_response(response: httpx.Response, posting: Posting) -> None`

However, you can have Posting call any function you wish using the syntax `path/to/script.py:function_to_run`.

Note that relative paths are relative to the collection directory.
This ensures that if you place scripts inside your collection directory,
they're included when you share a collection with others.

Note that you do not need to specify all of the arguments when writing these functions. Posting will only pass the number of arguments that you've specified when it calls your function. For example, you could define a your `on_request` function as `def on_request(request: RequestModel) -> None` and Posting would call it with `on_request(request: RequestModel)` without passing the `posting` argument.

## Editing scripts

When you edit a script, it'll automatically be reloaded.
This means you can keep Posting open while editing it.

Posting also allows you to quickly jump to your editor (assuming you've set the `$EDITOR` or `$POSTING_EDITOR` environment variables) to edit a script.
Press ++ctrl+e++ while a script input field inside the `Scripts` tab is focused to open the path in your editor.

!!! warning
    As of version 2.0.0, the script file must exist *before* pressing ++ctrl+e++. Posting will not create the file for you.

## Script logs

If your script writes to `stdout` or `stderr`, you'll see the output in the `Scripts` tab in the Response section.
This output is not persisted on disk.

### Example: Setup script

The **setup script** is run before the request is built.
You can set variables in the setup script that can be used in the request.
For example, you could use `httpx` to fetch an access token, then set the token as a variable so that it may be substituted into the request.

```python
def setup(posting: Posting) -> None:
    # Set a variable which may be used in this request
    # (or other requests to follow)
    posting.set_variable("auth_token", "1234567890")
```

With this setup script attached to a request, you can then reference the `auth_token` variable in the request UI by typing `$auth_token`.
The `$auth_token` variable will remain for the duration of the session,
so you may wish to add a check to see if it has already been set in this session:

```python
def setup(posting: Posting) -> None:
    if not posting.get_variable("auth_token"):
        posting.set_variable("auth_token", "1234567890")
```

### Example: Pre-request script

The **pre-request script** is run after the request has been constructed and variables have been substituted, right before the request is sent.

You can directly modify the `RequestModel` object in this function, for example to set headers, query parameters, etc.
The code snippet below shows some of the API.

```python
from posting import Auth, Header, RequestModel, Posting


def on_request(request: RequestModel, posting: Posting) -> None:
    # Add a custom header to the request.
    request.headers.append(Header(name="X-Custom-Header", value="foo"))

    # Set auth on the request.
    request.auth = Auth.basic_auth("username", "password")
    # request.auth = Auth.digest_auth("username", "password")
    # request.auth = Auth.bearer_token_auth("token")

    # This will be captured and written to the log.
    print("Request is being sent!")

    # Make a notification pop-up in the UI.
    posting.notify("Request is being sent!")
```

### Example: Post-response script

The **post-response script** is run after the response is received.
You can use this to extract data from the response, for example a JWT token,
and set it as a variable to be used in later requests.

```python
from posting import Posting


def on_response(response: httpx.Response, posting: Posting) -> None:
    # Print the status code of the response to the log.
    print(response.status_code)

    # Set a variable to be used in later requests.
    # You can write '$auth_token' in the UI and it will be substituted with
    # the value of the $auth_token variable.
    posting.set_variable("auth_token", response.headers["Authorization"])
```

### The `Posting` object

The `Posting` object provides access to the application context and useful methods:

- `set_variable(name: str, value: object) -> None`: Set a session variable
- `get_variable(name: str, default: object | None = None) -> object | None`: Get a session variable
- `clear_variable(name: str) -> None`: Clear a specific session variable
- `clear_all_variables() -> None`: Clear all session variables
- `notify(message: str, title: str = "", severity: str = "information", timeout: float | None = None)`: Send a notification to the user

Note that variables are described as "session variables" because they persist for the duration of the session (until you close Posting).

### Execution environment

Scripts run in the same process and environment as Posting, so you should take care to avoid performing damaging global operations such as monkey-patching standard library modules.

#### Libraries

You can make use of any library that is available in the Python environment that Posting is running in. This means you can use all of the Python standard library as well as any of Posting's dependencies (such as `httpx`, `pyyaml`, `pydantic`, etc).

If you install Posting with `uv`, you can easily add extra dependencies which you can then use in your scripts:

```bash
uv tool install posting --with <library-name>
```


================================================
FILE: docs/guide/themes.md
================================================
## Overview

Posting ships with several built-in themes, and also supports custom, user-made themes.

When editing a theme on disk, Posting can show a live preview of the theme in effect, making it easy to design and test themes.

### Creating a theme

You can check where Posting will look for user-defined themes by running `posting locate themes` in your terminal.
Place custom themes in this directory and Posting will load them on startup.
Theme files must be suffixed with `.yaml`, but the rest of the filename is unused by Posting.
Built-in themes are *not* in this directory, but are part of the Posting code itself.

Here's an example theme file:

```yaml
name: example  # use this name in your config file
primary: '#4e78c4'  # buttons, fixed table columns
secondary: '#f39c12'  # method selector, some minor labels
accent: '#e74c3c'  # header text, scrollbars, cursors, focus highlights
background: '#0e1726' # background colors
surface: '#17202a'  # panels, etc
error: '#e74c3c'  # error messages
success: '#2ecc71'  # success messages
warning: '#f1c40f'  # warning messages

# Optional metadata
author: Darren Burns
description: A dark theme with a blue primary color.
homepage: https://github.com/darrenburns/posting
```

After adding a theme, you'll need to restart Posting for it to take effect.

To use the theme, you can specify it in your `config.yaml` file:

```yaml
theme: example
```

Note that the theme name is *not* defined by the filename, but by the `name` field in the theme file.

!!! tip

    If you edit a theme on disk while Posting is using it, the UI will automatically
    refresh to reflect the changes you've made. This is enabled by default, but if you'd
    like to disable it, you can set `watch_themes` to `false` in your `config.yaml`.

#### Syntax highlighting

Syntax highlighted elements such as the URL bar, text areas, and fields which contain variables will be colored based on the semantic colors defined in the theme (`primary`, `secondary`, etc) by default.

If you'd like more control over the syntax highlighting, you can specify a custom syntax highlighting colors inside the theme file.

The example below illustrates some of the options available when it comes to customizing syntax highlighting.

```yaml
text_area:
  cursor: 'reverse'  # style the block cursor
  cursor_line: 'underline'  # style the line the cursor is on
  selection: 'reverse'  # style the selected text
  gutter: 'bold #50e3c2'  # style the gutter
  matched_bracket: 'black on green'  # style the matched bracket
url:
  base: 'italic #50e3c2'  # style the 'base' of the url
  protocol: 'bold #b8e986'  # style the protocol
syntax:
  json_key: 'italic #4a90e2'  # style json keys
  json_number: '#50e3c2'  # style json numbers
  json_string: '#b8e986'  # style json strings
  json_boolean: '#b8e986'  # style json booleans
  json_null: 'underline #b8e986'  # style json null values
```

#### Method styles

You can also specify custom styles for methods in the collection tree.

Here's an example:

```yaml
method:
  get: 'underline #50e3c2'
  post: 'italic #b8e986'
  put: 'bold #b8e986'
  delete: 'strikethrough #b8e986'
```

### X resources themes

Posting supports using X resources for theming. To use this, enable the `use_xresources` option (see above).

It requires the `xrdb` executable on your `PATH` and `xrdb -query` must return the following variables:

| Xresources  | Description |
|-------------|-----------|
| *color0     | primary color: used for button backgrounds and fixed table columns |
| *color8     | secondary color: used in method selector and some minor labels |
| *color1     | error color: used for error messages |
| *color2     | success color: used for success messages |
| *color3     | warning color: used for warning messages |
| *color4     | accent color: used for header text, scrollbars, cursors, focus highlights |
| *background | background color |
| *color7     | surface/panel color |

If these conditions are met, themes called `xresources-dark` and `xresources-light` will be available for use.


================================================
FILE: docs/index.md
================================================
---
title: The API client that lives in your terminal
template: home.html
---


================================================
FILE: docs/overrides/home.html
================================================
{% extends "main.html" %}
{% block tabs %}
{{ super() }}
<style>
    /* hide the main content on the home page.
    we're gonna do it all ourself. */
    html {
        scroll-behavior: smooth;
    }

    body {
        background-image: linear-gradient(0deg,
                hsl(272deg 33% 11%) 0%,
                hsl(276deg 30% 9%) 31%,
                hsl(279deg 29% 7%) 55%,
                hsl(282deg 24% 4%) 71%,
                hsl(313deg 30% 7%) 82%,
                hsl(307deg 47% 17%) 89%,
                hsl(305deg 62% 26%) 93%,
                hsl(304deg 80% 33%) 96%,
                hsl(311deg 94% 38%) 98%,
                hsl(318deg 98% 42%) 99%,
                hsl(324deg 100% 46%) 100%,
                hsl(329deg 100% 50%) 100%);
        background-repeat: no-repeat;
        background-attachment: fixed;
        background-size: auto;
    }

    h1,
    h2,
    h3,
    h4,
    h5,
    h6 {
        font-family: "Open Sans", sans-serif;
        font-weight: 800 !important;
    }

    /* .md-content{display:none} */
    .md-dialog {
        display: none;
    }

    .hero-text {
        padding: 0.4rem 1.8rem;
        text-align: left;
        max-width: 900px;
        margin: 0 auto;
    }

    .hero-image svg {
        width: 100%;
        /* Make SVG responsive */
        height: auto;
        /* Maintain aspect ratio */
        max-width: 1000px;
        /* Maximum width of SVG */
        max-height: 55vh;
        display: block;
        /* Center SVG */
        margin: 0 auto;
        /* Center SVG */
        padding: 1rem 1.2rem 1rem 1.2rem;
    }

    #home-title {
        margin: 0 0 0.3rem 0;
    }
    }

    p#home-intro {
        font-size: 0.9rem;
    }

    div.md-nav__source {
        background-color: hsla(287deg, 100%, 10%, 0.8) !important;
    }

    #home-subtitle {
        font-family: "Roboto Mono", monospace;
        font-size: 1rem;
        line-height: 1.5rem;
        opacity: 92%;
        margin: 0 0 1rem 0;
        color: #f0f0f0;
        word-spacing: -0.05em;
    }

    .button-bar {
        display: flex;
        flex-wrap: wrap;
        justify-content: flex-start;
        gap: 1rem;
    }

    .gradient-text {
        background: linear-gradient(to left, #fbb3ff, #ee66bf);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
    }

    .subtle-purple-glow {
        position: relative;
        z-index: 1;
        white-space: nowrap;
        display: inline-block;
    }

    .subtle-purple-glow::after {
        content: attr(data-text);
        position: absolute;
        left: 0;
        top: 0;
        z-index: -1;
        color: rgba(255, 100, 203, 1);
        /* Very light pink/purple */
        filter: blur(8px);
        -webkit-filter: blur(8px);
        white-space: nowrap;
    }

    #hero-section {
        max-height: 88vh;
    }

    h2.feature-title {
        font-family: "Roboto Mono", monospace;
        font-size: 1.4rem;
        font-weight: 550;
        color: #f0f0f0;
        margin-bottom: 0.5rem;
        margin-left: 0.65rem;
        margin-top: 0.5rem;
    }

    .feature-title-text {
        max-width: 80%;
    }

    .feature-title-svg {
        max-width: 20%;
    }

    #features-section {
        padding: 6rem 1.4rem;
        display: flex;
        flex-direction: column;
        gap: 4rem;
    }

    .feature-image {
        display: flex;
        align-items: center;
        justify-content: center;
    }

    .feature-image img,
    .feature-image video {
        width: 100%;
        height: auto;
        border-radius: 8px;
        border: solid 2px rgba(255, 100, 203, 0.33);
        margin-top: 0.8rem;
    }

    #features-section .feature-description {
        color: #d0d0d0;
        font-size: 0.84rem;
        border-left: solid 2px rgba(255, 100, 203, 0.33);
        padding-left: 12px;
    }

    .feature-columns {
        display: flex;
        flex-direction: column;
        gap: 2rem;
        padding: 2rem 1.4rem;
        position: relative;

        & .feature-column {
            position: relative;
            z-index: 1;

            & .feature-column-description {
                color: #d0d0d0;
                font-size: 0.9rem;
            }
        }
    }

    .feature-column::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: radial-gradient(circle at top, rgba(230, 32, 240, 0.34) 0%, rgba(160, 32, 240, 0) 70%);
        filter: blur(20px);
        opacity: 0.7;
        z-index: -1;
        transition: opacity 0.3s ease;
    }

    .feature-column:hover::before {
        opacity: 1;
    }


    .feature-column {
        background: rgba(255, 255, 255, 0.05);
        border-radius: 8px;
        padding: 1.5rem;
        border: 2px solid rgba(255, 100, 203, 0.2);
    }

    h3.feature-column-title {
        font-size: 1.2rem;
        font-weight: 550;
        color: #f0f0f0;
        margin: 0;
    }


    @media (min-width: 768px) {
        .feature-columns {
            flex-direction: row;
            justify-content: space-between;
        }

        .feature-column {
            flex-basis: calc(33.333% - 1.33rem);
        }
    }

    @media (min-width: 1024px) {
        .hero-text {
            padding: 0.4rem 2.4rem;
        }

        .hero-text h1 {
            font-size: 2rem;
            color: #f0f0f0;
        }

        #home-subtitle {
            font-size: 1.2rem;
            line-height: 1.7rem;
        }

        #home-intro {
            font-size: 1rem;
        }

        #features-section {
            max-width: 1200px;
            margin: 0 auto;
        }

        .feature-row {
            display: flex;
            align-items: space-between;
            gap: 2rem;
        }

        .feature-row:nth-child(even) {
            flex-direction: row-reverse;
        }

        .feature-text {
            flex: 1;
        }

        .feature-image {
            flex: 2;
        }

        .feature-image img,
        .feature-image video {
            margin-top: 0rem;
        }

        .feature-columns {
            max-width: 1200px;
            margin: 0 auto;
            padding: 4rem 2.4rem;
        }

        .feature-column-description {
            font-size: 1rem;
        }
    }

    @media (min-width: 1220px) {

        /* The sidebar content sits on top of the main content
        and makes some buttons unclickable. This is a hack to workaround
        that while keeping the sidebar working on screensizes where it's
        still required.*/
        body>div.md-container>main>div {
            display: none;
        }
    }

    @media (min-width: 1080px) {
        .feature-description {
            font-size: 1rem;
        }
    }
</style>
<section>
    <div id="hero-section" class="md-typeset">
        <div class="hero-image">
            <svg width="100%" height="100%" viewBox="0 0 1270 704" version="1.1" xmlns="http://www.w3.org/2000/svg"
                xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
                style="fill-rule:evenodd;clip-rule:evenodd;">
                <g transform="matrix(1,0,0,1,3.0232,-32.8644)">
                    <g transform="matrix(1,0,0,0.95092,0,35.9367)">
                        <path
                            d="M1262,9.413L1262,723.787C1262,728.43 1258.41,732.2 1254,732.2L9,732.2C4.585,732.2 1,728.43 1,723.787L1,9.413C1,4.77 4.585,1 9,1L1254,1C1258.41,1 1262,4.77 1262,9.413Z"
                            style="fill:rgb(15,15,31);stroke:url(#_Linear1);stroke-width:2px;" />
                    </g>
                    <g transform="matrix(1,0,0,1,9,41)">
                        <clipPath id="_clip2">
                            <rect x="0" y="0" width="1243.4" height="682.2" />
                        </clipPath>
                        <g clip-path="url(#_clip2)">
                            <rect x="0" y="1.5" width="1244.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="25.9" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="25.9" width="97.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="134.2" y="25.9" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="207.4" y="25.9" width="683.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="890.6" y="25.9" width="317.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="25.9" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="50.3" width="1244.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="74.7" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="74.7" width="12.2" height="24.65" style="fill:rgb(151,117,220);" />
                            <rect x="48.8" y="74.7" width="12.2" height="24.65" style="fill:rgb(151,117,220);" />
                            <rect x="61" y="74.7" width="36.6" height="24.65" style="fill:rgb(151,117,220);" />
                            <rect x="97.6" y="74.7" width="48.8" height="24.65" style="fill:rgb(151,117,220);" />
                            <rect x="146.4" y="74.7" width="12.2" height="24.65" style="fill:rgb(151,117,220);" />
                            <rect x="158.6" y="74.7" width="12.2" height="24.65" style="fill:rgb(151,117,220);" />
                            <rect x="170.8" y="74.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="183" y="74.7" width="61" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="244" y="74.7" width="36.6" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="280.6" y="74.7" width="341.6" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="622.2" y="74.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="634.4" y="74.7" width="329.4" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="963.8" y="74.7" width="24.4" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="988.2" y="74.7" width="85.4" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="1073.6" y="74.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="1085.8" y="74.7" width="24.4" height="24.65" style="fill:rgb(138,43,226);" />
                            <rect x="1110.2" y="74.7" width="73.2" height="24.65" style="fill:rgb(138,43,226);" />
                            <rect x="1183.4" y="74.7" width="24.4" height="24.65" style="fill:rgb(138,43,226);" />
                            <rect x="1207.8" y="74.7" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="99.1" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="99.1" width="1171.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="99.1" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="123.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="123.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="48.8" y="123.5" width="146.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="195.2" y="123.5" width="890.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1085.8" y="123.5" width="109.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="123.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="123.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="147.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="147.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="97.6" y="147.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="146.4" y="147.9" width="244" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="147.9" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="147.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="147.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="524.6" y="147.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="536.8" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="561.2" y="147.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="610" y="147.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="622.2" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="646.6" y="147.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="707.6" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="732" y="147.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="780.8" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="805.2" y="147.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="854" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="878.4" y="147.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="963.8" y="147.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="976" y="147.9" width="231.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="147.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="147.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="172.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="172.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="172.3" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="97.6" y="172.3" width="183" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="280.6" y="172.3" width="109.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="172.3" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="172.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="172.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="172.3" width="97.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="536.8" y="172.3" width="671" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="172.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="172.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="196.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="196.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="196.7" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="97.6" y="196.7" width="109.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="207.4" y="196.7" width="183" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="196.7" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="196.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="196.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="196.7" width="207.4" height="24.65" style="fill:rgb(119,38,196);" />
                            <rect x="646.6" y="196.7" width="268.4" height="24.65" style="fill:rgb(29,20,52);" />
                            <rect x="915" y="196.7" width="280.6" height="24.65" style="fill:rgb(28,18,52);" />
                            <rect x="1195.6" y="196.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="196.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="196.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="221.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="221.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="221.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="61" y="221.1" width="195.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="256.2" y="221.1" width="134.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="221.1" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="221.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="221.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="221.1" width="207.4" height="24.65" style="fill:rgb(88,31,148);" />
                            <rect x="646.6" y="221.1" width="268.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="915" y="221.1" width="292.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="221.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="221.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="245.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="245.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="245.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="61" y="245.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="245.5" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="158.6" y="245.5" width="231.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="245.5" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="245.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="245.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="245.5" width="207.4" height="24.65" style="fill:rgb(88,31,148);" />
                            <rect x="646.6" y="245.5" width="268.4" height="24.65" style="fill:rgb(33,19,60);" />
                            <rect x="915" y="245.5" width="280.6" height="24.65" style="fill:rgb(28,18,52);" />
                            <rect x="1195.6" y="245.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="245.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="245.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="269.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="269.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="269.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="269.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="146.4" y="269.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="231.8" y="269.9" width="158.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="269.9" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="269.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="269.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="269.9" width="207.4" height="24.65" style="fill:rgb(88,31,148);" />
                            <rect x="646.6" y="269.9" width="268.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="915" y="269.9" width="292.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="269.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="269.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="294.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="294.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="294.3" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="294.3" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="146.4" y="294.3" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="231.8" y="294.3" width="158.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="294.3" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="294.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="294.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="294.3" width="768.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="294.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="294.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="318.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="318.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="318.7" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="318.7" width="73.2" height="24.65" style="fill:rgb(47,45,81);" />
                            <rect x="158.6" y="318.7" width="73.2" height="24.65" style="fill:rgb(47,45,81);" />
                            <rect x="231.8" y="318.7" width="158.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="318.7" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="318.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="318.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="318.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="451.4" y="318.7" width="48.8" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="500.2" y="318.7" width="195.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="695.4" y="318.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="707.6" y="318.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="719.8" y="318.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="732" y="318.7" width="61" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="793" y="318.7" width="183" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="976" y="318.7" width="12.2" height="24.65" style="fill:rgb(30,30,63);" />
                            <rect x="988.2" y="318.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1000.4" y="318.7" width="24.4" height="24.65" style="fill:rgb(58,24,101);" />
                            <rect x="1024.8" y="318.7" width="146.4" height="24.65" style="fill:rgb(58,24,101);" />
                            <rect x="1171.2" y="318.7" width="24.4" height="24.65" style="fill:rgb(58,24,101);" />
                            <rect x="1195.6" y="318.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="318.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="318.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="343.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="343.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="343.1" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="343.1" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="146.4" y="343.1" width="158.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="305" y="343.1" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="343.1" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="343.1" width="817.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="343.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="367.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="367.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="367.5" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="367.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="109.8" y="367.5" width="109.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="219.6" y="367.5" width="170.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="367.5" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="367.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="367.5" width="488" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="902.8" y="367.5" width="122" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1024.8" y="367.5" width="158.6" height="24.65" style="fill:rgb(0,250,154);" />
                            <rect x="1183.4" y="367.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="367.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="367.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="391.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="391.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="391.9" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="109.8" y="391.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="170.8" y="391.9" width="146.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="317.2" y="391.9" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="391.9" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="391.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="391.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="391.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="391.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="488" y="391.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="512.4" y="391.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="597.8" y="391.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="622.2" y="391.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="707.6" y="391.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="732" y="391.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="793" y="391.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="805.2" y="391.9" width="402.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="391.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="391.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="416.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="416.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="416.3" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="109.8" y="416.3" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="170.8" y="416.3" width="219.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="416.3" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="416.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="416.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="416.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="416.3" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="488" y="416.3" width="719.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="416.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="416.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="440.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="440.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="440.7" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="109.8" y="440.7" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="170.8" y="440.7" width="170.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="341.6" y="440.7" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="440.7" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            <rect x="402.6" y="440.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="440.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="440.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="440.7" width="36.6" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="475.8" y="440.7" width="12.2" height="24.65" style="fill:rgb(82,82,88);" />
                            <rect x="488" y="440.7" width="707.6" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="1195.6" y="440.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="440.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="440.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="465.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="465.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="465.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="61" y="465.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="465.1" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="158.6" y="465.1" width="231.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <g transform="matrix(1,0,0,0.877927,0,56.776)">
                                <rect x="390.4" y="465.1" width="12.2" height="24.65" style="fill:rgb(85,41,86);" />
                            </g>
                            <rect x="402.6" y="465.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="465.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="465.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="465.1" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="475.8" y="465.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="500.2" y="465.1" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="585.6" y="465.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="610" y="465.1" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="671" y="465.1" width="524.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="465.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="465.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="465.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="489.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="489.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="489.5" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="489.5" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="146.4" y="489.5" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="231.8" y="489.5" width="158.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <g transform="matrix(1,0,0,1.15317,0,-78.7529)">
                                <rect x="390.4" y="489.5" width="12.2" height="24.65" style="fill:rgb(13,14,46);" />
                            </g>
                            <rect x="402.6" y="489.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="489.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="489.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="489.5" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="475.8" y="489.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="500.2" y="489.5" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="573.4" y="489.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="597.8" y="489.5" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="658.8" y="489.5" width="536.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="489.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="489.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="489.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="513.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="513.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="513.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="146.4" y="513.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="231.8" y="513.9" width="158.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="513.9" width="12.2" height="24.65" style="fill:rgb(13,14,46);" />
                            <rect x="402.6" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="513.9" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="475.8" y="513.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="500.2" y="513.9" width="97.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="597.8" y="513.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="622.2" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="634.4" y="513.9" width="561.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="513.9" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="513.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="538.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="538.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="538.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="61" y="538.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="85.4" y="538.3" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="158.6" y="538.3" width="231.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="390.4" y="538.3" width="12.2" height="24.65" style="fill:rgb(13,14,46);" />
                            <rect x="402.6" y="538.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="538.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="538.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="538.3" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="475.8" y="538.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="500.2" y="538.3" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="549" y="538.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="573.4" y="538.3" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="610" y="538.3" width="585.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="538.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="538.3" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="538.3" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="562.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="562.7" width="390.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="562.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="562.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="439.2" y="562.7" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="475.8" y="562.7" width="12.2" height="24.65" style="fill:rgb(82,82,88);" />
                            <rect x="488" y="562.7" width="707.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="562.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="562.7" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="562.7" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="587.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="36.6" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="48.8" y="587.1" width="207.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="256.2" y="587.1" width="146.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="402.6" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="587.1" width="366" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="793" y="587.1" width="36.6" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="829.6" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="841.8" y="587.1" width="109.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="951.6" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="963.8" y="587.1" width="12.2" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="976" y="587.1" width="48.8" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="1024.8" y="587.1" width="12.2" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="1037" y="587.1" width="12.2" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="1049.2" y="587.1" width="12.2" height="24.65" style="fill:rgb(24,24,39);" />
                            <rect x="1061.4" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1073.6" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1085.8" y="587.1" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1146.8" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1159" y="587.1" width="12.2" height="24.65" style="fill:rgb(48,48,59);" />
                            <rect x="1171.2" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1183.4" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1207.8" y="587.1" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="587.1" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="611.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="611.5" width="390.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="414.8" y="611.5" width="536.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="951.6" y="611.5" width="73.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1024.8" y="611.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1037" y="611.5" width="122" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1159" y="611.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1183.4" y="611.5" width="12.2" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1195.6" y="611.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="611.5" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="635.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="24.4" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="73.2" y="635.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="134.2" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="183" y="635.9" width="85.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="268.4" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="317.2" y="635.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="378.2" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="427" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="475.8" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="524.6" y="635.9" width="109.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="634.4" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="683.2" y="635.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="744.2" y="635.9" width="48.8" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="793" y="635.9" width="61" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="854" y="635.9" width="366" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="1220" y="635.9" width="24.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <rect x="0" y="660.3" width="1244.4" height="24.65" style="fill:rgb(15,15,31);" />
                            <g>
                                <g transform="matrix(1,0,0,1,36.6,44.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,147,221);">Posting</text>
                                </g>
                                <g transform="matrix(1,0,0,1,890.6,44.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(159,159,165);">darrenburns@posting.local</text>
                                </g>
                                <g transform="matrix(1,0,0,1,48.8,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(19,15,28);">P</text>
                                    <rect x="0" y="1.27" width="12.041" height="0.879" style="fill:rgb(19,15,28);" />
                                </g>
                                <g transform="matrix(1,0,0,1,61,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(19,15,28);">OST</text>
                                </g>
                                <g transform="matrix(1,0,0,1,146.4,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(60,46,88);">▼</text>
                                </g>
                                <g transform="matrix(1,0,0,1,183,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">https</text>
                                </g>
                                <g transform="matrix(1,0,0,1,244,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(147,147,163);">://</text>
                                </g>
                                <g transform="matrix(1,0,0,1,280.6,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(147,112,219);">jsonplaceholder.typicode.com</text>
                                </g>
                                <g transform="matrix(1,0,0,1,622.2,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(147,147,163);">/</text>
                                </g>
                                <g transform="matrix(1,0,0,1,634.4,93.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(225,225,230);">posts</text>
                                </g>
                                <g transform="matrix(1,0,0,1,982.356,93.9035)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(0,250,154);">■
                                        <tspan x="14.021px 28.042px 42.063px 56.084px 70.105px 84.126px "
                                            y="0px 0px 0px 0px 0px 0px ">■■■■■■</tspan>
                                    </text>
                                </g>
                                <g transform="matrix(1,0,0,1,1110.2,93.2)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(239,227,251);">Send</text>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,142)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">╭─</text>
                                </g>
                                <g transform="matrix(1,0,0,1,48.8,142)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">Collection</text>
                                </g>
                                <g transform="matrix(0.996034,0,0,1,198.687,142)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">─────────────────╮╭──────────────────────────────────────────────────────</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1085.8,142)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">Request</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1195.6,142)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">─╮</text>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,166.4)">
                                    <clipPath id="_clip3">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip3)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,36.6,166.4)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(14,165,233);">GET</text>
                                </g>
                                <g transform="matrix(1,0,0,1,97.6,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">echo</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,166.4)">
                                    <clipPath id="_clip4">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip4)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">Headers</text>
                                </g>
                                <g transform="matrix(1,0,0,1,524.6,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(88,209,235);">•</text>
                                </g>
                                <g transform="matrix(1,0,0,1,561.2,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Body</text>
                                </g>
                                <g transform="matrix(1,0,0,1,610,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(88,209,235);">•</text>
                                </g>
                                <g transform="matrix(1,0,0,1,646.6,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Query</text>
                                </g>
                                <g transform="matrix(1,0,0,1,732,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Auth</text>
                                </g>
                                <g transform="matrix(1,0,0,1,805.2,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Info</text>
                                </g>
                                <g transform="matrix(1,0,0,1,878.4,166.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Options</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,166.4)">
                                    <clipPath id="_clip5">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip5)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,190.8)">
                                    <clipPath id="_clip6">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip6)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,36.6,190.8)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(14,165,233);">GET</text>
                                </g>
                                <g transform="matrix(1,0,0,1,97.6,190.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">get random user</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,190.8)">
                                    <clipPath id="_clip7">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip7)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,190.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(37,37,50);">╸</text>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,190.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">━━━━━━━━</text>
                                </g>
                                <g transform="matrix(1,0,0,1,536.8,190.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(37,37,50);">╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,190.8)">
                                    <clipPath id="_clip8">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip8)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,215.2)">
                                    <clipPath id="_clip9">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip9)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,36.6,215.2)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(34,197,94);">POS</text>
                                </g>
                                <g transform="matrix(1,0,0,1,97.6,215.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">echo post</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,215.2)">
                                    <clipPath id="_clip10">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip10)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,215.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(15,15,31);">▐</text>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,215.2)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(237,226,247);">Content-Type</text>
                                </g>
                                <g transform="matrix(1,0,0,1,646.6,215.2)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(225,224,228);">application/json</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,215.2)">
                                    <clipPath id="_clip11">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip11)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,239.6)">
                                    <clipPath id="_clip12">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip12)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,36.6,239.6)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(139,139,147);">▼</text>
                                </g>
                                <g transform="matrix(1,0,0,1,61,239.6)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(139,139,147);">jsonplaceholder/</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,239.6)">
                                    <clipPath id="_clip13">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip13)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,239.6)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(15,15,31);">▐</text>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,239.6)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(233,225,241);">Referer</text>
                                </g>
                                <g transform="matrix(1,0,0,1,646.6,239.6)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">https://example.com/</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,239.6)">
                                    <clipPath id="_clip14">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip14)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,264)">
                                    <clipPath id="_clip15">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip15)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,61,264)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(139,139,147);">▼</text>
                                </g>
                                <g transform="matrix(1,0,0,1,85.4,264)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(139,139,147);">posts/</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,264)">
                                    <clipPath id="_clip16">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip16)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,264)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(15,15,31);">▐</text>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,264)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(233,225,241);">Accept-Encoding</text>
                                </g>
                                <g transform="matrix(1,0,0,1,646.6,264)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(226,224,229);">gzip</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,264)">
                                    <clipPath id="_clip17">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip17)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,288.4)">
                                    <clipPath id="_clip18">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip18)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,85.4,288.4)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(14,165,233);">GET</text>
                                </g>
                                <g transform="matrix(1,0,0,1,146.4,288.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">get all</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,288.4)">
                                    <clipPath id="_clip19">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip19)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,288.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(15,15,31);">▐</text>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,288.4)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(233,225,241);">Cache-Control</text>
                                </g>
                                <g transform="matrix(1,0,0,1,646.6,288.4)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">no-cache</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,288.4)">
                                    <clipPath id="_clip20">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip20)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,312.8)">
                                    <clipPath id="_clip21">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip21)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,85.4,312.8)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(14,165,233);">GET</text>
                                </g>
                                <g transform="matrix(1,0,0,1,146.4,312.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">get one</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,312.8)">
                                    <clipPath id="_clip22">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip22)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,312.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(15,15,31);">▐</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,312.8)">
                                    <clipPath id="_clip23">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip23)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,337.2)">
                                    <clipPath id="_clip24">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip24)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,85.4,337.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(155,154,171);">█ POS</text>
                                </g>
                                <g transform="matrix(1,0,0,1,158.6,337.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(227,227,232);">create</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,337.2)">
                                    <clipPath id="_clip25">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip25)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">││</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,451.4,337.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(115,115,135);">Name</text>
                                </g>
                                <g transform="matrix(1,0,0,1,732,337.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(115,115,135);">Value</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1024.8,337.2)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(145,141,157);">Add header</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,337.2)">
                                    <clipPath id="_clip26">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip26)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,361.6)">
                                    <clipPath id="_clip27">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip27)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,85.4,361.6)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(239,68,68);">DEL</text>
                                </g>
                                <g transform="matrix(1,0,0,1,146.4,361.6)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">delete a post</text>
                                </g>
                                <g transform="matrix(1.01332,0,0,1,402.53,361.6)">
                                    <clipPath id="_clip28">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip28)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│╰────────────────────────────────────────────────────────────────╯</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,386)">
                                    <clipPath id="_clip29">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip29)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,85.4,386)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(139,139,147);">▼</text>
                                </g>
                                <g transform="matrix(1,0,0,1,109.8,386)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(139,139,147);">comments/</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,386)">
                                    <clipPath id="_clip30">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip30)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,414.8,386)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">╭───────────────────────────────────────</text>
                                </g>
                                <g transform="matrix(1,0,0,1,902.8,386)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">Response</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1024.8,386)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(0,32,20);">201 Created</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1195.6,386)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">─╮</text>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,410.4)">
                                    <clipPath id="_clip31">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip31)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,109.8,410.4)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(14,165,233);">GET</text>
                                </g>
                                <g transform="matrix(1,0,0,1,170.8,410.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">get comments</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,410.4)">
                                    <clipPath id="_clip32">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip32)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,414.8,410.4)">
                                    <clipPath id="_clip33">
                                        <rect x="-414.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip33)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,410.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">Body</text>
                                </g>
                                <g transform="matrix(1,0,0,1,512.4,410.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Headers</text>
                                </g>
                                <g transform="matrix(1,0,0,1,622.2,410.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Cookies</text>
                                </g>
                                <g transform="matrix(1,0,0,1,732,410.4)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(106,106,116);">Trace</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,410.4)">
                                    <clipPath id="_clip34">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip34)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,434.8)">
                                    <clipPath id="_clip35">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip35)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,109.8,434.8)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(14,165,233);">GET</text>
                                </g>
                                <g transform="matrix(1,0,0,1,170.8,434.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">get comments (via</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,434.8)">
                                    <clipPath id="_clip36">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip36)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,414.8,434.8)">
                                    <clipPath id="_clip37">
                                        <rect x="-414.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip37)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,427,434.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(135,60,105);">╸</text>
                                </g>
                                <g transform="matrix(1,0,0,1,439.2,434.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">━━━━</text>
                                </g>
                                <g transform="matrix(1,0,0,1,488,434.8)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(135,60,105);">╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</text>
                                </g>
                                <g transform="matrix(1,0,0,1,1207.8,434.8)">
                                    <clipPath id="_clip38">
                                        <rect x="-1207.8" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip38)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(255,105,180);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,24.4,459.2)">
                                    <clipPath id="_clip39">
                                        <rect x="-24.4" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip39)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(99,46,83);">│</text>
                                    </g>
                                </g>
                                <g transform="matrix(1,0,0,1,109.8,459.2)">
                                    <text x="12.041px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(245,158,11);">PUT</text>
                                </g>
                                <g transform="matrix(1,0,0,1,170.8,459.2)">
                                    <text x="0px" y="0px"
                                        style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20px;fill:rgb(223,223,225);">edit a comment</text>
                                </g>
                                <g transform="matrix(1,0,0,1,402.6,459.2)">
                                    <clipPath id="_clip40">
                                        <rect x="-402.6" y="-18.5" width="1244.4" height="24.65" />
                                    </clipPath>
                                    <g clip-path="url(#_clip40)">
                                        <text x="0px" y="0px"
                                            style="font-family:'Menlo-Regular', 'Menlo', monospace;font-size:20
Download .txt
gitextract_4f7kaixp/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── coverage.yml
│       ├── docs.yml
│       └── test.yml
├── .gitignore
├── .python-version
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── docs/
│   ├── CHANGELOG.md
│   ├── CNAME
│   ├── faq.md
│   ├── guide/
│   │   ├── collections.md
│   │   ├── command_palette.md
│   │   ├── configuration.md
│   │   ├── environments.md
│   │   ├── external_tools.md
│   │   ├── help_system.md
│   │   ├── importing.md
│   │   ├── index.md
│   │   ├── keymap.md
│   │   ├── navigation.md
│   │   ├── requests.md
│   │   ├── scripting.md
│   │   └── themes.md
│   ├── index.md
│   ├── overrides/
│   │   └── home.html
│   ├── roadmap.md
│   └── stylesheets/
│       └── extra.css
├── mkdocs.yml
├── pyproject.toml
├── src/
│   └── posting/
│       ├── __init__.py
│       ├── __main__.py
│       ├── _start_time.py
│       ├── app.py
│       ├── auth.py
│       ├── collection.py
│       ├── commands.py
│       ├── config.py
│       ├── exit_codes.py
│       ├── files.py
│       ├── help_data.py
│       ├── help_screen.py
│       ├── highlight_url.py
│       ├── highlighters.py
│       ├── importing/
│       │   ├── curl.py
│       │   ├── open_api.py
│       │   └── postman.py
│       ├── jump_overlay.py
│       ├── jumper.py
│       ├── locations.py
│       ├── messages.py
│       ├── posting.scss
│       ├── request_headers.py
│       ├── save_request.py
│       ├── scripts.py
│       ├── suggesters.py
│       ├── themes.py
│       ├── tuple_to_multidict.py
│       ├── types.py
│       ├── urls.py
│       ├── user_host.py
│       ├── variables.py
│       ├── version.py
│       ├── widgets/
│       │   ├── __init__.py
│       │   ├── center_middle.py
│       │   ├── collection/
│       │   │   ├── browser.py
│       │   │   └── new_request_modal.py
│       │   ├── confirmation.py
│       │   ├── datatable.py
│       │   ├── input.py
│       │   ├── key_value.py
│       │   ├── request/
│       │   │   ├── __init__.py
│       │   │   ├── form_editor.py
│       │   │   ├── header_editor.py
│       │   │   ├── method_selection.py
│       │   │   ├── path_editor.py
│       │   │   ├── query_editor.py
│       │   │   ├── request_auth.py
│       │   │   ├── request_body.py
│       │   │   ├── request_editor.py
│       │   │   ├── request_metadata.py
│       │   │   ├── request_options.py
│       │   │   ├── request_scripts.py
│       │   │   └── url_bar.py
│       │   ├── response/
│       │   │   ├── cookies_table.py
│       │   │   ├── response_area.py
│       │   │   ├── response_body.py
│       │   │   ├── response_headers.py
│       │   │   ├── response_trace.py
│       │   │   └── script_output.py
│       │   ├── rich_log.py
│       │   ├── select.py
│       │   ├── tabbed_content.py
│       │   ├── text_area.py
│       │   ├── tree.py
│       │   ├── variable_autocomplete.py
│       │   └── variable_input.py
│       ├── xresources.py
│       └── yaml.py
└── tests/
    ├── posting_snapshot_app.py
    ├── resources/
    │   └── snapshot_report_template.jinja2
    ├── sample-collections/
    │   ├── echo-post-01.posting.yaml
    │   ├── echo.posting.yaml
    │   ├── get-random-user.posting.yaml
    │   ├── jsonplaceholder/
    │   │   ├── posts/
    │   │   │   ├── comments/
    │   │   │   │   ├── edit.posting.yaml
    │   │   │   │   ├── get-comments-query.posting.yaml
    │   │   │   │   └── get-comments.posting.yaml
    │   │   │   ├── create.posting.yaml
    │   │   │   ├── delete.posting.yaml
    │   │   │   ├── get-all.posting.yaml
    │   │   │   └── get-one.posting.yaml
    │   │   ├── todos/
    │   │   │   ├── get-all.posting.yaml
    │   │   │   └── get-one.posting.yaml
    │   │   └── users/
    │   │       ├── create.posting.yaml
    │   │       ├── delete.posting.yaml
    │   │       ├── get-all.posting.yaml
    │   │       ├── get-one.posting.yaml
    │   │       └── update.posting.yaml
    │   └── scripts/
    │       └── my_script.py
    ├── sample-configs/
    │   ├── custom_theme.yaml
    │   ├── custom_theme2.yaml
    │   ├── general.yaml
    │   └── modified_config.yaml
    ├── sample-envs/
    │   ├── sample_base.env
    │   └── sample_extra.env
    ├── sample-importable-collections/
    │   ├── Fixer.postman_collection.json
    │   ├── postman_collection.json
    │   └── test-postman-collection.json
    ├── sample-themes/
    │   ├── another_test.yml
    │   └── serene_ocean.yaml
    ├── test_curl_export.py
    ├── test_curl_import.py
    ├── test_files.py
    ├── test_open_api_import.py
    ├── test_postman_import.py
    ├── test_snapshots.py
    ├── test_urls.py
    └── test_variables.py
Download .txt
SYMBOL INDEX (792 symbols across 68 files)

FILE: src/posting/__main__.py
  function create_config_file (line 21) | def create_config_file() -> None:
  function create_default_collection (line 32) | def create_default_collection() -> Path:
  function cli (line 46) | def cli() -> None:
  function default (line 64) | def default(collection: str | None = None, env: tuple[str, ...] = ()) ->...
  function locate (line 80) | def locate(thing_to_locate: str) -> None:
  function import_spec (line 108) | def import_spec(spec_path: str, output: str | None, type: str) -> None:
  function sponsors (line 178) | def sponsors() -> None:
  function make_posting (line 194) | def make_posting(

FILE: src/posting/app.py
  class AppHeader (line 93) | class AppHeader(Horizontal):
    method compose (line 96) | def compose(self) -> ComposeResult:
  class AppBody (line 108) | class AppBody(Vertical):
  class MainScreen (line 112) | class MainScreen(Screen[None]):
    method __init__ (line 196) | def __init__(
    method on_mount (line 211) | def on_mount(self) -> None:
    method on_screen_resume (line 232) | def on_screen_resume(self) -> None:
    method compose (line 255) | def compose(self) -> ComposeResult:
    method get_and_run_script (line 271) | def get_and_run_script(
    method send_request (line 354) | async def send_request(self) -> None:
    method send_via_worker (line 513) | async def send_via_worker(self) -> None:
    method on_method_selector_changed (line 517) | def on_method_selector_changed(self, event: MethodSelector.MethodChang...
    method handle_submit_via_event (line 522) | def handle_submit_via_event(self) -> None:
    method on_response_received (line 527) | def on_response_received(self, event: HttpResponseReceived) -> None:
    method on_request_selected (line 545) | def on_request_selected(self, event: CollectionTree.RequestSelected) -...
    method on_request_cache_updated (line 561) | def on_request_cache_updated(
    method on_url_param_jump (line 568) | def on_url_param_jump(
    method on_path_param_jump (line 606) | def on_path_param_jump(
    method on_path_param_renamed (line 626) | def on_path_param_renamed(self, event: PathParamsEditor.PathParamRenam...
    method action_send_request (line 663) | async def action_send_request(self) -> None:
    method action_change_method (line 667) | def action_change_method(self) -> None:
    method action_toggle_collection_browser (line 673) | def action_toggle_collection_browser(self) -> None:
    method action_toggle_expanded (line 678) | def action_toggle_expanded(self) -> None:
    method expand_section (line 693) | def expand_section(self, section: Literal["request", "response"] | Non...
    method focus_within_request (line 696) | def focus_within_request(self, focused: Widget | None = None) -> bool:
    method focus_within_response (line 704) | def focus_within_response(self, focused: Widget | None = None) -> bool:
    method action_save_request (line 712) | async def action_save_request(self) -> None:
    method action_new_request (line 748) | async def action_new_request(self) -> None:
    method watch_current_layout (line 752) | def watch_current_layout(self, layout: Literal["horizontal", "vertical...
    method watch_expanded_section (line 759) | def watch_expanded_section(
    method on_request_body_change (line 772) | def on_request_body_change(self, event: RequestBodyTextArea.Changed) -...
    method on_content_changed (line 782) | def on_content_changed(
    method on_params_changed (line 794) | def on_params_changed(
    method on_path_params_changed (line 806) | def on_path_params_changed(
    method on_path_param_values_updated (line 817) | def on_path_param_values_updated(
    method build_httpx_request (line 824) | def build_httpx_request(
    method on_url_changed (line 836) | def on_url_changed(self, event: Input.Changed) -> None:
    method _sync_path_params_from_url (line 857) | def _sync_path_params_from_url(
    method log_request_trace_event (line 901) | async def log_request_trace_event(self, event: Event, info: dict[str, ...
    method build_request_model (line 906) | def build_request_model(self, request_options: Options) -> RequestModel:
    method on_curl_message (line 950) | def on_curl_message(self, event: CurlMessage):
    method action_open_request_search_palette (line 969) | def action_open_request_search_palette(self) -> None:
    method load_request_model (line 999) | def load_request_model(
    method action_toggle_jump_mode (line 1061) | def action_toggle_jump_mode(self) -> None:
    method watch__jumping (line 1064) | def watch__jumping(self, jumping: bool) -> None:
    method url_bar (line 1125) | def url_bar(self) -> UrlBar:
    method footer (line 1129) | def footer(self) -> Footer:
    method method_selector (line 1133) | def method_selector(self) -> MethodSelector:
    method url_input (line 1137) | def url_input(self) -> UrlInput:
    method request_editor (line 1141) | def request_editor(self) -> RequestEditor:
    method response_area (line 1145) | def response_area(self) -> ResponseArea:
    method request_body_text_area (line 1149) | def request_body_text_area(self) -> RequestBodyTextArea:
    method headers_table (line 1153) | def headers_table(self) -> HeadersTable:
    method params_table (line 1157) | def params_table(self) -> ParamsTable:
    method path_params_table (line 1161) | def path_params_table(self) -> PathParamsTable:
    method app_body (line 1165) | def app_body(self) -> AppBody:
    method app_header (line 1169) | def app_header(self) -> AppHeader:
    method request_options (line 1173) | def request_options(self) -> RequestOptions:
    method request_metadata (line 1177) | def request_metadata(self) -> RequestMetadata:
    method collection_browser (line 1181) | def collection_browser(self) -> CollectionBrowser:
    method request_auth (line 1185) | def request_auth(self) -> RequestAuth:
    method request_scripts (line 1189) | def request_scripts(self) -> RequestScripts:
    method collection_tree (line 1193) | def collection_tree(self) -> CollectionTree:
    method response_trace (line 1197) | def response_trace(self) -> ResponseTrace:
    method response_script_output (line 1201) | def response_script_output(self) -> ScriptOutput:
  class Posting (line 1205) | class Posting(App[None], inherit_bindings=False):
    method __init__ (line 1239) | def __init__(
    method on_ready (line 1285) | def on_ready(self) -> None:
    method watch_spacing (line 1292) | def watch_spacing(self, spacing: Literal["standard", "compact"]) -> None:
    method watch_environment_files (line 1303) | async def watch_environment_files(self) -> None:
    method watch_collection_files (line 1331) | async def watch_collection_files(self) -> None:
    method watch_themes (line 1362) | async def watch_themes(self) -> None:
    method on_mount (line 1387) | def on_mount(self) -> None:
    method get_default_screen (line 1450) | def get_default_screen(self) -> MainScreen:
    method command_layout (line 1458) | def command_layout(self, layout: Literal["vertical", "horizontal"]) ->...
    method command_toggle_spacing (line 1461) | def command_toggle_spacing(self) -> None:
    method action_open_web_docs (line 1464) | def action_open_web_docs(self) -> None:
    method command_export_to_curl (line 1469) | def command_export_to_curl(self, run_setup_scripts: bool = True) -> None:
    method command_copy_request_yaml (line 1532) | def command_copy_request_yaml(self) -> None:
    method action_save_screenshot (line 1573) | def action_save_screenshot(
    method palette_opened (line 1579) | def palette_opened(self) -> None:
    method palette_option_highlighted (line 1589) | def palette_option_highlighted(
    method palette_closed (line 1608) | def palette_closed(self, event: CommandPalette.Closed) -> None:
    method search_commands (line 1617) | def search_commands(
    method action_help (line 1640) | async def action_help(self) -> None:
    method exit (line 1652) | def exit(

FILE: src/posting/auth.py
  class HttpxBearerTokenAuth (line 6) | class HttpxBearerTokenAuth(httpx.Auth):
    method __init__ (line 7) | def __init__(self, token: str):
    method auth_flow (line 10) | def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request...

FILE: src/posting/collection.py
  class Auth (line 23) | class Auth(BaseModel):
    method to_httpx_auth (line 29) | def to_httpx_auth(self) -> httpx.Auth | None:
    method basic_auth (line 42) | def basic_auth(cls, username: str, password: str) -> Auth:
    method digest_auth (line 46) | def digest_auth(cls, username: str, password: str) -> Auth:
    method bearer_token_auth (line 52) | def bearer_token_auth(cls, token: str) -> Auth:
  class BasicAuth (line 56) | class BasicAuth(BaseModel):
  class DigestAuth (line 61) | class DigestAuth(BaseModel):
  class BearerTokenAuth (line 66) | class BearerTokenAuth(BaseModel):
  class PathParam (line 70) | class PathParam(BaseModel):
  class Header (line 75) | class Header(BaseModel):
  class FormItem (line 81) | class FormItem(BaseModel):
  class QueryParam (line 87) | class QueryParam(BaseModel):
  class Cookie (line 93) | class Cookie(BaseModel):
    method from_httpx (line 99) | def from_httpx(cls, cookies: httpx.Cookies) -> list[Cookie]:
  class Options (line 103) | class Options(BaseModel):
  class RequestBody (line 111) | class RequestBody(BaseModel):
    method to_httpx_args (line 121) | def to_httpx_args(self) -> dict[str, Any]:
  function request_sort_key (line 133) | def request_sort_key(request: RequestModel) -> tuple[int, str]:
  class Scripts (line 138) | class Scripts(BaseModel):
  class RequestModel (line 155) | class RequestModel(BaseModel):
    method apply_template (line 205) | def apply_template(self, variables: dict[str, Any]) -> None:
    method to_httpx (line 269) | def to_httpx(self, client: httpx.AsyncClient) -> httpx.Request:
    method save_to_disk (line 291) | def save_to_disk(self, path: Path) -> None:
    method delete_from_disk (line 303) | def delete_from_disk(self) -> None:
    method to_curl (line 312) | def to_curl(self, extra_args: str = "") -> str:
    method __lt__ (line 392) | def __lt__(self, other: RequestModel) -> bool:
    method __eq__ (line 395) | def __eq__(self, other: object) -> bool:
  class Contact (line 401) | class Contact(BaseModel):
  class License (line 407) | class License(BaseModel):
  class ExternalDocs (line 412) | class ExternalDocs(BaseModel):
  class APIInfo (line 417) | class APIInfo(BaseModel):
  class Collection (line 427) | class Collection(BaseModel):
    method from_openapi_spec (line 435) | def from_openapi_spec(
    method generate_readme (line 442) | def generate_readme(
    method from_directory (line 481) | def from_directory(cls, directory: str) -> Collection:
    method save_to_disk (line 541) | def save_to_disk(self, path: Path) -> None:
  function load_request_from_yaml (line 553) | def load_request_from_yaml(file_path: str) -> RequestModel:

FILE: src/posting/commands.py
  class PostingProvider (line 13) | class PostingProvider(Provider):
    method commands (line 15) | def commands(
    method discover (line 194) | async def discover(self) -> Hits:
    method search (line 208) | async def search(self, query: str) -> Hits:
    method posting (line 228) | def posting(self) -> "Posting":

FILE: src/posting/config.py
  class HeadingSettings (line 18) | class HeadingSettings(BaseModel):
  class UrlBarSettings (line 34) | class UrlBarSettings(BaseModel):
  class ResponseSettings (line 46) | class ResponseSettings(BaseModel):
  class FocusSettings (line 56) | class FocusSettings(BaseModel):
  class CertificateSettings (line 79) | class CertificateSettings(BaseModel):
  class TextInputSettings (line 92) | class TextInputSettings(BaseModel):
  class CommandPaletteSettings (line 99) | class CommandPaletteSettings(BaseModel):
  class CollectionBrowserSettings (line 106) | class CollectionBrowserSettings(BaseModel):
  class Settings (line 116) | class Settings(BaseSettings):
    method settings_customise_sources (line 219) | def settings_customise_sources(

FILE: src/posting/files.py
  function is_valid_filename (line 10) | def is_valid_filename(filename: str) -> bool:
  function get_request_file_stem_and_suffix (line 62) | def get_request_file_stem_and_suffix(file_name: str) -> tuple[str, str]:
  function request_file_exists (line 72) | def request_file_exists(file_name: str, parent_directory: Path) -> bool:
  function get_unique_request_filename (line 99) | def get_unique_request_filename(file_name: str, parent_directory: Path) ...

FILE: src/posting/help_data.py
  class HelpData (line 5) | class HelpData:

FILE: src/posting/help_screen.py
  class Helpable (line 15) | class Helpable(Protocol):
  class HelpModalHeader (line 22) | class HelpModalHeader(Label):
  class HelpModalFooter (line 33) | class HelpModalFooter(Label):
  class HelpModalFocusNote (line 44) | class HelpModalFocusNote(Label):
  class HelpScreen (line 48) | class HelpScreen(ModalScreen[None]):
    method __init__ (line 117) | def __init__(
    method compose (line 127) | def compose(self) -> ComposeResult:

FILE: src/posting/highlighters.py
  function highlight_url (line 20) | def highlight_url(text: Text, styles: UrlStyles) -> None:
  function highlight_variables (line 35) | def highlight_variables(text: Text, styles: VariableStyles) -> None:
  function highlight_path_params (line 48) | def highlight_path_params(
  class VariableHighlighter (line 60) | class VariableHighlighter(Highlighter):
    method __init__ (line 61) | def __init__(self, variable_styles: VariableStyles | None = None) -> N...
    method highlight (line 65) | def highlight(self, text: Text) -> None:
  class VariablesAndUrlHighlighter (line 71) | class VariablesAndUrlHighlighter(Highlighter):
    method __init__ (line 72) | def __init__(self, input: Input) -> None:
    method set_path_params (line 79) | def set_path_params(self, params: dict[str, str]) -> None:
    method highlight (line 87) | def highlight(self, text: Text) -> None:

FILE: src/posting/importing/curl.py
  class CurlImport (line 22) | class CurlImport:
    method __init__ (line 27) | def __init__(self, curl_command: str):
    method parse_data (line 156) | def parse_data(self, data_str: str) -> list[tuple[str, str]]:
    method parse_form (line 170) | def parse_form(self, form_list: list[str]) -> list[tuple[str, str]]:
    method _extract_auth_from_headers (line 183) | def _extract_auth_from_headers(self) -> tuple[Auth | None, list[tuple[...
    method to_request_model (line 261) | def to_request_model(self) -> RequestModel:

FILE: src/posting/importing/open_api.py
  function resolve_url_variables (line 40) | def resolve_url_variables(url: str, variables: dict[str, dict[str, str]]...
  function generate_unique_env_filename (line 55) | def generate_unique_env_filename(base_name: str, server_url: str) -> str:
  function extract_server_variables (line 75) | def extract_server_variables(spec: dict[str, Any]) -> dict[str, dict[str...
  function security_scheme_to_variables (line 90) | def security_scheme_to_variables(
  function security_scheme_to_auth (line 117) | def security_scheme_to_auth(
  function generate_readme (line 139) | def generate_readme(
  function create_env_file (line 194) | def create_env_file(
  function parse_schema_ref (line 215) | def parse_schema_ref(ref: str, openapi: OpenAPI) -> Schema | None:
  class JsonBodyGenerator (line 224) | class JsonBodyGenerator:
    method __init__ (line 225) | def __init__(self, openapi: OpenAPI):
    method generate_json (line 230) | def generate_json(self, src: Reference | Schema | MediaType):
    method generate (line 236) | def generate(self, src: Reference | Schema | MediaType):
    method _any_from_schema (line 262) | def _any_from_schema(self, schema: Schema):
  function import_openapi_spec (line 283) | def import_openapi_spec(spec_path: str | Path) -> Collection:

FILE: src/posting/importing/postman.py
  class Variable (line 24) | class Variable(BaseModel):
  class RawRequestOptions (line 34) | class RawRequestOptions(BaseModel):
  class RequestOptions (line 38) | class RequestOptions(BaseModel):
  class Body (line 42) | class Body(BaseModel):
  class Url (line 49) | class Url(BaseModel):
  class PostmanRequest (line 56) | class PostmanRequest(BaseModel):
  class RequestItem (line 64) | class RequestItem(BaseModel):
  class PostmanCollection (line 70) | class PostmanCollection(BaseModel):
  function sanitize_variables (line 77) | def sanitize_variables(string: str) -> str:
  function sanitize_str (line 82) | def sanitize_str(string: str) -> str:
  function create_env_file (line 91) | def create_env_file(path: Path, env_filename: str, variables: list[Varia...
  function format_request (line 102) | def format_request(name: str, request: PostmanRequest) -> RequestModel:
  function process_item (line 178) | def process_item(
  function import_postman_spec (line 203) | def import_postman_spec(

FILE: src/posting/jump_overlay.py
  class JumpOverlay (line 16) | class JumpOverlay(ModalScreen[str | Widget | None]):
    method __init__ (line 32) | def __init__(
    method on_key (line 46) | def on_key(self, key_event: events.Key) -> None:
    method action_dismiss_overlay (line 64) | def action_dismiss_overlay(self) -> None:
    method on_resize (line 67) | async def on_resize(self) -> None:
    method on_unmount (line 80) | async def on_unmount(self) -> None:
    method _debounced_recompose (line 84) | async def _debounced_recompose(self) -> None:
    method _sync (line 103) | def _sync(self) -> None:
    method compose (line 107) | def compose(self) -> ComposeResult:

FILE: src/posting/jumper.py
  class Jumpable (line 10) | class Jumpable(Protocol):
  class JumpInfo (line 16) | class JumpInfo(NamedTuple):
  class Jumper (line 26) | class Jumper:
    method __init__ (line 29) | def __init__(self, ids_to_keys: Mapping[str, str], screen: Screen[Any]...
    method get_overlays (line 34) | def get_overlays(self) -> dict[Offset, JumpInfo]:

FILE: src/posting/locations.py
  function _posting_directory (line 6) | def _posting_directory(root: Path) -> Path:
  function data_directory (line 12) | def data_directory() -> Path:
  function theme_directory (line 17) | def theme_directory() -> Path:
  function default_collection_directory (line 24) | def default_collection_directory() -> Path:
  function config_directory (line 29) | def config_directory() -> Path:
  function config_file (line 34) | def config_file() -> Path:

FILE: src/posting/messages.py
  class HttpResponseReceived (line 7) | class HttpResponseReceived(Message):

FILE: src/posting/request_headers.py
  class RequestHeader (line 4) | class RequestHeader(TypedDict):

FILE: src/posting/save_request.py
  function slugify (line 8) | def slugify(text: str) -> str:
  function generate_request_filename (line 15) | def generate_request_filename(request_title: str) -> str:

FILE: src/posting/scripts.py
  class Posting (line 23) | class Posting:
    method __init__ (line 26) | def __init__(self, app: PostingApp):
    method variables (line 37) | def variables(self) -> dict[str, object]:
    method get_variable (line 45) | def get_variable(self, name: str, default: object | None = None) -> ob...
    method set_variable (line 59) | def set_variable(self, name: str, value: object) -> None:
    method clear_variable (line 71) | def clear_variable(self, name: str) -> None:
    method clear_all_variables (line 81) | def clear_all_variables(self) -> None:
    method notify (line 86) | def notify(
  function clear_module_cache (line 113) | def clear_module_cache():
  function execute_script (line 121) | def execute_script(
  function _import_script_as_module (line 164) | def _import_script_as_module(
  function _validate_function (line 191) | def _validate_function(func: Any) -> Callable[..., Any] | None:
  function uncache_module (line 204) | def uncache_module(script_path: str) -> None:

FILE: src/posting/themes.py
  class PostingTextAreaTheme (line 14) | class PostingTextAreaTheme(BaseModel):
  class SyntaxTheme (line 34) | class SyntaxTheme(BaseModel):
  class VariableStyles (line 54) | class VariableStyles(BaseModel):
    method fill_with_defaults (line 63) | def fill_with_defaults(self, theme: "Theme") -> "VariableStyles":
  class UrlStyles (line 72) | class UrlStyles(BaseModel):
    method fill_with_defaults (line 84) | def fill_with_defaults(self, theme: "Theme") -> "UrlStyles":
  class MethodStyles (line 94) | class MethodStyles(BaseModel):
  class Theme (line 106) | class Theme(BaseModel):
    method to_textual_theme (line 144) | def to_textual_theme(self) -> TextualTheme:
    method text_area_theme_from_theme_variables (line 245) | def text_area_theme_from_theme_variables(
  class UserThemeLoadResult (line 305) | class UserThemeLoadResult(NamedTuple):
  function load_user_themes (line 314) | def load_user_themes() -> UserThemeLoadResult:
  function load_user_theme (line 341) | def load_user_theme(path: Path) -> TextualTheme | None:

FILE: src/posting/tuple_to_multidict.py
  function tuples_to_dict (line 9) | def tuples_to_dict(tuple_list: list[tuple[K, V]]) -> dict[K, list[V]]:

FILE: src/posting/urls.py
  function ensure_protocol (line 9) | def ensure_protocol(url: str) -> str:
  function extract_path_param_names (line 24) | def extract_path_param_names(url: str) -> list[str]:
  function substitute_path_params (line 49) | def substitute_path_params(url: str, params: dict[str, str]) -> str:

FILE: src/posting/user_host.py
  function get_user_host_string (line 9) | def get_user_host_string() -> Text:

FILE: src/posting/variables.py
  class SharedVariables (line 15) | class SharedVariables:
    method __init__ (line 16) | def __init__(self):
    method get (line 19) | def get(self) -> dict[str, object]:
    method set (line 22) | def set(self, variables: dict[str, object]) -> None:
    method update (line 25) | def update(self, new_variables: dict[str, object]) -> None:
  function get_variables (line 32) | def get_variables() -> dict[str, object]:
  function load_variables (line 36) | def load_variables(
  function update_variables (line 73) | def update_variables(new_variables: dict[str, object]) -> None:
  function find_variables (line 86) | def find_variables(template_str: str) -> list[tuple[str, int, int]]:
  function variable_range_at_cursor (line 95) | def variable_range_at_cursor(cursor: int, text: str) -> tuple[int, int] ...
  function is_cursor_within_variable (line 108) | def is_cursor_within_variable(cursor: int, text: str) -> bool:
  function find_variable_start (line 112) | def find_variable_start(cursor: int, text: str) -> int:
  function find_variable_end (line 117) | def find_variable_end(cursor: int, text: str) -> int:
  function get_variable_at_cursor (line 125) | def get_variable_at_cursor(cursor: int, text: str) -> str | None:
  function extract_variable_name (line 134) | def extract_variable_name(variable_text: str) -> str:
  class SubstitutionError (line 157) | class SubstitutionError(Exception):

FILE: src/posting/widgets/center_middle.py
  class CenterMiddle (line 4) | class CenterMiddle(Widget, inherit_bindings=False):

FILE: src/posting/widgets/collection/browser.py
  class CollectionTree (line 40) | class CollectionTree(PostingTree[CollectionNode]):
    method __init__ (line 93) | def __init__(
    class RequestAdded (line 114) | class RequestAdded(Message):
      method control (line 120) | def control(self) -> "CollectionTree":
    class RequestSelected (line 124) | class RequestSelected(Message):
      method control (line 130) | def control(self) -> "CollectionTree":
    class RequestCacheUpdated (line 134) | class RequestCacheUpdated(Message):
      method control (line 139) | def control(self) -> "CollectionTree":
    method watch_currently_open (line 144) | def watch_currently_open(self, node: TreeNode[CollectionNode] | None) ...
    method render_label (line 154) | def render_label(
    method scroll_to_line (line 228) | def scroll_to_line(self, line: int, animate: bool = False) -> None:
    method on_mount (line 240) | def on_mount(self) -> None:
    method on_node_selected (line 249) | def on_node_selected(self, event: Tree.NodeSelected[CollectionNode]) -...
    method new_request_flow (line 256) | async def new_request_flow(self, templated_from: RequestModel | None) ...
    method add_request (line 417) | def add_request(
    method action_duplicate_request (line 441) | async def action_duplicate_request(self) -> None:
    method action_quick_duplicate_request (line 452) | def action_quick_duplicate_request(self) -> None:
    method action_delete_request (line 484) | def action_delete_request(self) -> None:
    method action_delete_request_with_confirmation (line 494) | async def action_delete_request_with_confirmation(self) -> None:
    method cache_request (line 526) | def cache_request(self, request: RequestModel) -> None:
  class RequestPreview (line 552) | class RequestPreview(VerticalScroll):
    method compose (line 555) | def compose(self) -> ComposeResult:
    method watch_request (line 559) | def watch_request(self, request: RequestModel | None) -> None:
  class CollectionBrowser (line 566) | class CollectionBrowser(Vertical):
    method __init__ (line 567) | def __init__(
    method compose (line 578) | def compose(self) -> ComposeResult:
    method on_request_added (line 622) | def on_request_added(self, event: CollectionTree.RequestAdded) -> None:
    method on_request_selected (line 628) | def on_request_selected(self, event: CollectionTree.RequestSelected) -...
    method on_node_highlighted (line 633) | def on_node_highlighted(self, event: Tree.NodeHighlighted[CollectionNo...
    method update_currently_open_node (line 647) | def update_currently_open_node(self, request_model: RequestModel) -> N...
    method request_preview (line 660) | def request_preview(self) -> RequestPreview:
    method collection_tree (line 664) | def collection_tree(self) -> CollectionTree:

FILE: src/posting/widgets/collection/new_request_modal.py
  class NewRequestData (line 22) | class NewRequestData:
  class FileNameValidator (line 35) | class FileNameValidator(Validator):
    method validate (line 36) | def validate(self, value: str) -> ValidationResult:
  class DirectoryValidator (line 44) | class DirectoryValidator(Validator):
    method validate (line 45) | def validate(self, value: str) -> ValidationResult:
  class NewRequestModal (line 58) | class NewRequestModal(ModalScreen[NewRequestData | None]):
    method __init__ (line 111) | def __init__(
    method compose (line 128) | def compose(self) -> ComposeResult:
    method action_close_screen (line 190) | def action_close_screen(self) -> None:
    method on_title_changed (line 194) | def on_title_changed(self, event: Input.Changed) -> None:
    method on_create (line 210) | def on_create(self, event: Input.Submitted | Button.Pressed) -> None:
    method action_create_request (line 213) | def action_create_request(self) -> None:
    method validate_and_create_request (line 216) | def validate_and_create_request(
    method file_name_input (line 301) | def file_name_input(self) -> Input:
    method title_input (line 305) | def title_input(self) -> Input:
    method description_textarea (line 309) | def description_textarea(self) -> PostingTextArea:
    method directory_input (line 313) | def directory_input(self) -> Input:

FILE: src/posting/widgets/confirmation.py
  class ConfirmationModal (line 12) | class ConfirmationModal(ModalScreen[bool]):
    method __init__ (line 39) | def __init__(
    method on_mount (line 59) | def on_mount(self) -> None:
    method compose (line 66) | def compose(self) -> ComposeResult:
    method confirm (line 75) | def confirm(self) -> None:
    method cancel (line 79) | def cancel(self) -> None:
    method action_move_focus (line 82) | def action_move_focus(self) -> None:

FILE: src/posting/widgets/datatable.py
  class PostingDataTable (line 19) | class PostingDataTable(DataTable[str | Text]):
    method __init__ (line 40) | def __init__(self, *args: Any, **kwargs: Any):
    class Checkbox (line 50) | class Checkbox:
      method __rich__ (line 57) | def __rich__(self) -> RenderResult:
      method toggle (line 60) | def toggle(self) -> bool:
      method plain (line 67) | def plain(self) -> str:
    class RowsRemoved (line 71) | class RowsRemoved(Message):
      method control (line 76) | def control(self) -> "PostingDataTable":
    class RowsAdded (line 80) | class RowsAdded(Message):
      method control (line 85) | def control(self) -> "PostingDataTable":
    method add_row (line 88) | def add_row(
    method action_toggle_fixed_columns (line 116) | def action_toggle_fixed_columns(self) -> None:
    method remove_row (line 119) | def remove_row(self, row_key: RowKey | str) -> None:
    method clear (line 125) | def clear(self, columns: bool = False) -> Self:
    method replace_all_rows (line 131) | def replace_all_rows(
    method column_width_refresh (line 147) | def column_width_refresh(self) -> None:
    method action_cursor_down (line 156) | def action_cursor_down(self) -> None:
    method action_cursor_up (line 174) | def action_cursor_up(self) -> None:
    method _on_rows_removed (line 191) | def _on_rows_removed(self, event: RowsRemoved | RowsAdded) -> None:
    method action_remove_row (line 194) | def action_remove_row(self) -> None:
    method action_toggle_row (line 202) | def action_toggle_row(self) -> None:
    method toggle_row (line 210) | def toggle_row(self, row_key: RowKey) -> None:
    method is_row_enabled_at (line 222) | def is_row_enabled_at(self, row_index: int) -> bool:
    method render_line (line 231) | def render_line(self, y: int) -> Strip:
    method _on_row_label_selected (line 254) | def _on_row_label_selected(self, event: DataTable.RowLabelSelected) ->...
    method _on_click (line 259) | async def _on_click(self, event: events.Click) -> None:
    method post_message (line 265) | def post_message(self, message: Message) -> bool:
    method __rich_repr__ (line 270) | def __rich_repr__(self):

FILE: src/posting/widgets/input.py
  class PostingInput (line 9) | class PostingInput(Input):
    method on_mount (line 10) | def on_mount(self) -> None:
    method cursor_style (line 19) | def cursor_style(self) -> Style:
    method on_theme_change (line 26) | def on_theme_change(self, theme: Theme) -> None:

FILE: src/posting/widgets/key_value.py
  class KeyValueInput (line 19) | class KeyValueInput(Horizontal):
    class Change (line 21) | class Change(Message):
      method control (line 27) | def control(self) -> "KeyValueInput":
    method __init__ (line 32) | def __init__(
    method compose (line 52) | def compose(self) -> ComposeResult:
    method watch_edit_mode (line 66) | def watch_edit_mode(self, edit_mode: bool) -> None:
    method submit_allowed (line 75) | def submit_allowed(self) -> bool:
    method determine_button_enabled (line 81) | def determine_button_enabled(self) -> None:
    method add_pair (line 87) | def add_pair(self, event: Input.Submitted | Button.Pressed) -> None:
    method button (line 123) | def button(self) -> Button:
  class KeyValueEditor (line 127) | class KeyValueEditor(Vertical):
    method __init__ (line 137) | def __init__(
    method on_mount (line 158) | def on_mount(self) -> None:
    method compose (line 161) | def compose(self) -> ComposeResult:
    method on_theme_change (line 167) | def on_theme_change(self, _) -> None:
    method check_action (line 175) | def check_action(self, action: str, parameters: tuple[object, ...]) ->...
    method add_key_value_pair (line 181) | def add_key_value_pair(self, event: KeyValueInput.Change) -> None:
    method rows_removed (line 206) | def rows_removed(self, event: PostingDataTable.RowsRemoved) -> None:
    method rows_added (line 217) | def rows_added(self, event: PostingDataTable.RowsAdded) -> None:
    method row_selected (line 222) | def row_selected(self, event: PostingDataTable.RowSelected) -> None:
    method get_row_to_edit (line 232) | def get_row_to_edit(self) -> RowKey | None:
    method highlight_and_retrieve_row_values (line 245) | def highlight_and_retrieve_row_values(self, row_key: RowKey) -> tuple[...
    method action_edit_row (line 266) | def action_edit_row(self, input_to_focus: str) -> None:
    method action_cancel_edit_row (line 276) | def action_cancel_edit_row(self) -> None:
    method enter_edit_mode (line 280) | def enter_edit_mode(self, row_key: RowKey, focus_value: bool = False) ...
    method exit_edit_mode (line 304) | def exit_edit_mode(self, revert: bool = False) -> None:

FILE: src/posting/widgets/request/form_editor.py
  class FormTable (line 12) | class FormTable(PostingDataTable):
    method on_mount (line 18) | def on_mount(self):
    method to_model (line 26) | def to_model(self) -> list[FormItem]:
  class FormEditor (line 40) | class FormEditor(Vertical):
    method compose (line 43) | def compose(self) -> ComposeResult:
    method to_model (line 53) | def to_model(self) -> list[FormItem]:
    method replace_all_rows (line 56) | def replace_all_rows(

FILE: src/posting/widgets/request/header_editor.py
  class HeaderInput (line 184) | class HeaderInput(PostingInput):
  class HeaderEditor (line 198) | class HeaderEditor(Vertical):
    method compose (line 201) | def compose(self) -> ComposeResult:
    method on_mount (line 217) | def on_mount(self):
    method get_header_value_candidates (line 233) | def get_header_value_candidates(
    method header_key_input (line 244) | def header_key_input(self) -> HeaderInput:
  class HeadersTable (line 248) | class HeadersTable(PostingDataTable):
    method on_mount (line 271) | def on_mount(self):
    method watch_has_focus (line 279) | def watch_has_focus(self, value: bool) -> None:
    method as_dict (line 283) | def as_dict(self) -> dict[str, str]:
    method to_model (line 291) | def to_model(self) -> list[Header]:

FILE: src/posting/widgets/request/method_selection.py
  class MethodSelector (line 14) | class MethodSelector(PostingSelect[str]):
    method __init__ (line 37) | def __init__(
    class MethodChanged (line 69) | class MethodChanged(Message):
      method control (line 74) | def control(self) -> "MethodSelector":
    method method_selected (line 78) | def method_selected(self, event: Select.Changed) -> None:
    method action_select_method (line 85) | def action_select_method(self, method: str) -> None:

FILE: src/posting/widgets/request/path_editor.py
  class PathParamsTable (line 17) | class PathParamsTable(PostingDataTable):
    class PathParamJumpRequestedFromPathParamsTable (line 25) | class PathParamJumpRequestedFromPathParamsTable(Message):
      method control (line 30) | def control(self) -> "PathParamsTable":
    method on_mount (line 39) | def on_mount(self):
    method action_remove_row (line 47) | def action_remove_row(self) -> None:
    method action_jump_to_url_param (line 51) | def action_jump_to_url_param(self) -> None:
    method to_model (line 66) | def to_model(self) -> list[PathParam]:
  class PathParamsEditor (line 79) | class PathParamsEditor(KeyValueEditor):
    class PathParamsUpdated (line 85) | class PathParamsUpdated(Message):
    class PathParamRenamed (line 89) | class PathParamRenamed(Message):
    method __init__ (line 93) | def __init__(self) -> None:
    method on_mount (line 111) | def on_mount(self) -> None:
    method add_key_value_pair (line 116) | def add_key_value_pair(self, event: KeyValueInput.Change) -> None:
    method enter_edit_mode (line 138) | def enter_edit_mode(self, row_key: RowKey, focus_value: bool = False) ...
    method exit_edit_mode (line 146) | def exit_edit_mode(self, revert: bool = False) -> None:
    method _get_params (line 158) | def _get_params(self) -> dict[str, str]:
  class PathEditor (line 168) | class PathEditor(Vertical):
    method compose (line 173) | def compose(self) -> ComposeResult:
    method path_key_input (line 177) | def path_key_input(self) -> Input:

FILE: src/posting/widgets/request/query_editor.py
  class ParamsTable (line 13) | class ParamsTable(PostingDataTable):
    method on_mount (line 23) | def on_mount(self):
    method watch_has_focus (line 31) | def watch_has_focus(self, value: bool) -> None:
    method to_model (line 35) | def to_model(self) -> list[QueryParam]:
  class QueryStringEditor (line 49) | class QueryStringEditor(Vertical):
    method compose (line 54) | def compose(self) -> ComposeResult:
    method query_key_input (line 66) | def query_key_input(self) -> Input:

FILE: src/posting/widgets/request/request_auth.py
  class Form (line 18) | class Form(Protocol):
    method get_values (line 22) | def get_values(self) -> dict[str, str]:
  class UserNamePasswordForm (line 27) | class UserNamePasswordForm(Vertical):
    method compose (line 38) | def compose(self) -> ComposeResult:
    method set_values (line 47) | def set_values(self, username: str, password: str) -> None:
    method get_values (line 51) | def get_values(self) -> dict[str, str]:
  class BearerTokenForm (line 58) | class BearerTokenForm(Vertical):
    method compose (line 75) | def compose(self) -> ComposeResult:
    method on_mount (line 90) | def on_mount(self) -> None:
    method on_input_changed (line 95) | def on_input_changed(self, event: Input.Changed) -> None:
    method set_values (line 101) | def set_values(self, token: str) -> None:
    method get_values (line 104) | def get_values(self) -> dict[str, str]:
    method token_input (line 110) | def token_input(self) -> Input:
  class RequestAuth (line 114) | class RequestAuth(VerticalScroll):
    method compose (line 145) | def compose(self) -> ComposeResult:
    method on_auth_type_changed (line 173) | def on_auth_type_changed(self, event: Select.Changed):
    method to_httpx_auth (line 177) | def to_httpx_auth(self) -> httpx.Auth | None:
    method to_model (line 192) | def to_model(self) -> Auth | None:
    method load_auth (line 222) | def load_auth(self, auth: Auth | None) -> None:
    method content_switcher (line 263) | def content_switcher(self) -> ContentSwitcher:
    method current_form (line 267) | def current_form(self) -> Form | None:

FILE: src/posting/widgets/request/request_body.py
  class RequestBodyEditor (line 12) | class RequestBodyEditor(Vertical):
    method compose (line 17) | def compose(self) -> ComposeResult:
  class RequestBodyTextArea (line 50) | class RequestBodyTextArea(PostingTextArea):
    method on_mount (line 67) | def on_mount(self):

FILE: src/posting/widgets/request/request_editor.py
  class RequestEditorTabbedContent (line 29) | class RequestEditorTabbedContent(PostingTabbedContent):
  class RequestEditor (line 33) | class RequestEditor(Vertical):
    method compose (line 38) | def compose(self) -> ComposeResult:
    method on_mount (line 60) | def on_mount(self):
    method request_body_type_changed (line 65) | def request_body_type_changed(self, event: Select.Changed) -> None:
    method request_body_type_select (line 70) | def request_body_type_select(self) -> Select[str]:
    method request_body_content_switcher (line 74) | def request_body_content_switcher(self) -> ContentSwitcher:
    method text_editor (line 78) | def text_editor(self) -> TextEditor:
    method form_editor (line 82) | def form_editor(self) -> FormEditor:
    method query_editor (line 86) | def query_editor(self) -> QueryStringEditor:
    method to_request_model_args (line 89) | def to_request_model_args(self) -> dict[str, Any]:

FILE: src/posting/widgets/request/request_metadata.py
  class RequestMetadata (line 11) | class RequestMetadata(VerticalScroll):
    method watch_request (line 14) | def watch_request(self, request: RequestModel | None) -> None:
    method compose (line 27) | def compose(self) -> ComposeResult:
    method request_name_input (line 39) | def request_name_input(self) -> Input:
    method request_description_textarea (line 43) | def request_description_textarea(self) -> PostingTextArea:
    method request_path_text_area (line 47) | def request_path_text_area(self) -> ReadOnlyTextArea:
    method request_name (line 51) | def request_name(self) -> str:
    method description (line 55) | def description(self) -> str:

FILE: src/posting/widgets/request/request_options.py
  class RequestOptions (line 12) | class RequestOptions(VerticalScroll):
    method __init__ (line 62) | def __init__(self):
    method compose (line 76) | def compose(self) -> ComposeResult:
    method on_checkbox_change (line 111) | def on_checkbox_change(self, event: Checkbox.Changed) -> None:
    method on_proxy_url_changed (line 124) | def on_proxy_url_changed(self, event: Input.Changed) -> None:
    method on_timeout_changed (line 129) | def on_timeout_changed(self, event: Input.Changed) -> None:
    method on_descendant_focus (line 137) | def on_descendant_focus(self, event: DescendantFocus) -> None:
    method to_model (line 148) | def to_model(self) -> Options:
    method load_options (line 152) | def load_options(self, options: Options) -> None:
    method follow_redirects_checkbox (line 167) | def follow_redirects_checkbox(self) -> Checkbox:
    method verify_ssl_checkbox (line 171) | def verify_ssl_checkbox(self) -> Checkbox:
    method attach_cookies_checkbox (line 175) | def attach_cookies_checkbox(self) -> Checkbox:
    method proxy_url_input (line 179) | def proxy_url_input(self) -> Input:
    method timeout_input (line 183) | def timeout_input(self) -> Input:

FILE: src/posting/widgets/request/request_scripts.py
  class ScriptPathInput (line 17) | class ScriptPathInput(Input):
    method __init__ (line 33) | def __init__(
    method _get_script_path (line 42) | def _get_script_path(self) -> Path | None:
    method _open_with_command (line 73) | def _open_with_command(self, command_name: str, command_setting: str) ...
    method action_open_in_editor (line 112) | def action_open_in_editor(self) -> None:
    method action_open_in_pager (line 120) | def action_open_in_pager(self) -> None:
  class RequestScripts (line 129) | class RequestScripts(VerticalScroll):
    method __init__ (line 169) | def __init__(
    method compose (line 183) | def compose(self) -> ComposeResult:
    method on_mount (line 207) | def on_mount(self) -> None:
    method get_script_candidates (line 225) | def get_script_candidates(self, state: TargetState) -> list[DropdownIt...
    method load_scripts (line 231) | def load_scripts(self, scripts: Scripts) -> None:
    method to_model (line 236) | def to_model(self) -> Scripts:

FILE: src/posting/widgets/request/url_bar.py
  class CurlMessage (line 33) | class CurlMessage(Message):
    method __init__ (line 34) | def __init__(self, curl_command: str) -> None:
  class UrlInput (line 39) | class UrlInput(PostingInput):
    class CursorMoved (line 66) | class CursorMoved(Message):
      method control (line 72) | def control(self) -> "UrlInput":
    class PathParamJumpRequestedFromUrlInput (line 76) | class PathParamJumpRequestedFromUrlInput(Message):
      method control (line 81) | def control(self) -> "UrlInput":
    method on_mount (line 84) | def on_mount(self):
    method on_change (line 92) | def on_change(self, event: Input.Changed) -> None:
    method watch_selection (line 95) | def watch_selection(self, selection: Selection) -> None:
    method on_theme_change (line 98) | def on_theme_change(self, theme: Theme) -> None:
    method on_paste (line 117) | def on_paste(self, event: Paste):
    method action_jump_to_path_param (line 123) | def action_jump_to_path_param(self) -> None:
  class SendRequestButton (line 137) | class SendRequestButton(Button, can_focus=False):
  class UrlBar (line 143) | class UrlBar(Vertical):
    method __init__ (line 162) | def __init__(
    method on_env_changed (line 173) | def on_env_changed(self, _: None) -> None:
    method watch_response_status_code (line 177) | def watch_response_status_code(self, status_code: int | None) -> None:
    method watch_response_reason_phrase (line 193) | def watch_response_reason_phrase(self, reason_phrase: str | None) -> N...
    method compose (line 199) | def compose(self) -> ComposeResult:
    method on_mount (line 215) | def on_mount(self) -> None:
    method on_change (line 228) | def on_change(self, event: Input.Changed) -> None:
    method on_blur (line 235) | def on_blur(self, event: Input.Blurred) -> None:
    method on_cursor_moved (line 242) | def on_cursor_moved(self, event: UrlInput.CursorMoved) -> None:
    method _display_variable_at_cursor (line 245) | def _display_variable_at_cursor(self) -> None:
    method _get_autocomplete_candidates (line 277) | def _get_autocomplete_candidates(
    method _get_variable_candidates (line 282) | def _get_variable_candidates(self, target_state: TargetState) -> list[...
    method on_theme_change (line 285) | def on_theme_change(self, theme: Theme) -> None:
    method log_event (line 291) | def log_event(self, event: Event, info: dict[str, Any]) -> None:
    method _build_markers (line 299) | def _build_markers(self) -> Text:
    method clear_events (line 328) | def clear_events(self) -> None:
    method trace_markers (line 334) | def trace_markers(self) -> Label:
    method variable_value_bar (line 339) | def variable_value_bar(self) -> Label:
    method url_input (line 344) | def url_input(self) -> UrlInput:
    method status_code_label (line 349) | def status_code_label(self) -> Label:

FILE: src/posting/widgets/response/cookies_table.py
  class CookiesSection (line 9) | class CookiesSection(Vertical):
    method compose (line 10) | def compose(self) -> ComposeResult:
    method on_mount (line 14) | def on_mount(self) -> None:
    method rows_removed (line 22) | def rows_removed(self, event: PostingDataTable.RowsRemoved) -> None:
    method rows_added (line 27) | def rows_added(self, event: PostingDataTable.RowsAdded) -> None:
    method table (line 32) | def table(self) -> PostingDataTable:

FILE: src/posting/widgets/response/response_area.py
  class ResponseTabbedContent (line 21) | class ResponseTabbedContent(PostingTabbedContent):
  class ResponseArea (line 25) | class ResponseArea(Vertical):
    method on_mount (line 36) | def on_mount(self) -> None:
    method compose (line 42) | def compose(self) -> ComposeResult:
    method on_theme_change (line 59) | def on_theme_change(self, _) -> None:
    method watch_response (line 63) | def watch_response(self, response: httpx.Response | None) -> None:
    method _make_border_title (line 119) | def _make_border_title(self, response: httpx.Response) -> str:
    method text_editor (line 124) | def text_editor(self) -> TextEditor:
    method headers_table (line 128) | def headers_table(self) -> ResponseHeadersTable:
    method cookies_section (line 132) | def cookies_section(self) -> CookiesSection:
    method tabbed_content (line 136) | def tabbed_content(self) -> ResponseTabbedContent:
    method content_tabs (line 140) | def content_tabs(self) -> ContentTabs:
  function content_type_to_language (line 144) | def content_type_to_language(content_type: str) -> str | None:
  function human_readable_size (line 160) | def human_readable_size(size: float, decimal_places: int = 2) -> str:  #...

FILE: src/posting/widgets/response/response_body.py
  class ResponseTextArea (line 8) | class ResponseTextArea(ReadOnlyTextArea):
    method on_change (line 30) | def on_change(self, event: TextArea.Changed) -> None:

FILE: src/posting/widgets/response/response_headers.py
  class ResponseHeadersTable (line 4) | class ResponseHeadersTable(PostingDataTable):
    method on_mount (line 5) | def on_mount(self) -> None:

FILE: src/posting/widgets/response/response_trace.py
  class ResponseTrace (line 33) | class ResponseTrace(VerticalScroll):
    method __init__ (line 40) | def __init__(
    method compose (line 50) | def compose(self) -> ComposeResult:
    method log_event (line 67) | async def log_event(self, event_name: Event, info: dict[str, Any]) -> ...
    method trace_complete (line 82) | def trace_complete(self) -> None:

FILE: src/posting/widgets/response/script_output.py
  class ScriptOutput (line 20) | class ScriptOutput(VerticalScroll):
    method compose (line 34) | def compose(self) -> ComposeResult:
    method set_setup_status (line 50) | def set_setup_status(self, status: ScriptStatus) -> None:
    method set_request_status (line 55) | def set_request_status(self, status: ScriptStatus) -> None:
    method set_response_status (line 60) | def set_response_status(self, status: ScriptStatus) -> None:
    method set_label_status (line 65) | def set_label_status(self, label_id: str, status: ScriptStatus) -> None:
    method reset (line 84) | def reset(self) -> None:
    method log_function_call_start (line 91) | def log_function_call_start(self, function: str) -> None:
    method rich_log (line 96) | def rich_log(self) -> RichLog:

FILE: src/posting/widgets/rich_log.py
  class RichLogIO (line 7) | class RichLogIO(StringIO):
    method __init__ (line 8) | def __init__(self, rich_log: RichLog, stream_type: Literal["stdout", "...
    method write (line 14) | def write(self, s: str) -> int:
    method _flush_line (line 24) | def _flush_line(self, line: str) -> None:
    method flush (line 30) | def flush(self) -> None:
  class PostingRichLog (line 37) | class PostingRichLog(RichLog):

FILE: src/posting/widgets/select.py
  class PostingSelect (line 9) | class PostingSelect(Select[T], inherit_bindings=False):
    method action_cursor_up (line 16) | def action_cursor_up(self):
    method action_cursor_down (line 22) | def action_cursor_down(self):
    method select_overlay (line 29) | def select_overlay(self) -> SelectOverlay:

FILE: src/posting/widgets/tabbed_content.py
  class PostingTabbedContent (line 5) | class PostingTabbedContent(TabbedContent):
    method action_next_tab (line 13) | def action_next_tab(self) -> None:
    method action_previous_tab (line 18) | def action_previous_tab(self) -> None:

FILE: src/posting/widgets/text_area.py
  class TextAreaFooter (line 30) | class TextAreaFooter(Horizontal):
    class LanguageChanged (line 41) | class LanguageChanged(Message):
      method control (line 46) | def control(self) -> "TextAreaFooter":
    class SoftWrapChanged (line 50) | class SoftWrapChanged(Message):
      method control (line 55) | def control(self) -> "TextAreaFooter":
    method __init__ (line 64) | def __init__(
    method watch_selection (line 81) | def watch_selection(self, selection: Selection) -> None:
    method watch_visual_mode (line 85) | def watch_visual_mode(self, value: bool) -> None:
    method watch_read_only (line 90) | def watch_read_only(self, value: bool) -> None:
    method compose (line 95) | def compose(self) -> ComposeResult:
    method update_language (line 116) | def update_language(self, event: Select.Changed) -> None:
    method update_soft_wrap (line 124) | def update_soft_wrap(self, event: Checkbox.Changed) -> None:
    method cursor_location_label (line 130) | def cursor_location_label(self) -> Label:
    method action_focus_text_area (line 133) | def action_focus_text_area(self) -> None:
  class PostingTextArea (line 137) | class PostingTextArea(TextArea):
    method on_mount (line 151) | def on_mount(self) -> None:
    method on_theme_change (line 166) | def on_theme_change(self, theme: TextualTheme) -> None:
    method on_change (line 188) | def on_change(self, event: TextArea.Changed) -> None:
    method action_open_in_editor (line 192) | def action_open_in_editor(self) -> None:
    method action_open_in_pager (line 204) | def action_open_in_pager(self) -> None:
    method _open_as_tempfile (line 230) | def _open_as_tempfile(self, command: str) -> None:
    method on_key (line 264) | def on_key(self, event: events.Key) -> None:
    method get_content_start_column (line 338) | def get_content_start_column(self, line: str) -> int:
    method _yield_character_locations_reverse (line 346) | def _yield_character_locations_reverse(
  class ReadOnlyTextArea (line 366) | class ReadOnlyTextArea(PostingTextArea):
    method __init__ (line 422) | def __init__(
    class VisualModeToggled (line 456) | class VisualModeToggled(Message):
      method control (line 461) | def control(self) -> TextArea:
    method action_toggle_visual_mode (line 466) | def action_toggle_visual_mode(self):
    method watch_visual_mode (line 469) | def watch_visual_mode(self, value: bool) -> None:
    method action_cursor_up (line 477) | def action_cursor_up(self, select: bool = False) -> None:
    method action_cursor_right (line 480) | def action_cursor_right(self, select: bool = False) -> None:
    method action_cursor_down (line 483) | def action_cursor_down(self, select: bool = False) -> None:
    method action_cursor_left (line 486) | def action_cursor_left(self, select: bool = False) -> None:
    method action_cursor_line_end (line 489) | def action_cursor_line_end(self, select: bool = False) -> None:
    method action_cursor_line_start (line 492) | def action_cursor_line_start(self, select: bool = False) -> None:
    method action_cursor_word_left (line 495) | def action_cursor_word_left(self, select: bool = False) -> None:
    method action_cursor_word_right (line 498) | def action_cursor_word_right(self, select: bool = False) -> None:
    method action_copy_to_clipboard (line 501) | def action_copy_to_clipboard(self) -> None:
    method action_cursor_top (line 527) | def action_cursor_top(self) -> None:
    method action_cursor_bottom (line 530) | def action_cursor_bottom(self) -> None:
    method action_cursor_to_matched_bracket (line 533) | def action_cursor_to_matched_bracket(self) -> None:
    method action_cursor_half_page_down (line 551) | def action_cursor_half_page_down(self) -> None:
    method action_cursor_half_page_up (line 562) | def action_cursor_half_page_up(self) -> None:
    method on_focus (line 573) | def on_focus(self) -> None:
    method get_content_start_column (line 577) | def get_content_start_column(self, line: str) -> int:
    method _yield_character_locations_reverse (line 585) | def _yield_character_locations_reverse(
  class TextEditor (line 602) | class TextEditor(Vertical):
    method __init__ (line 607) | def __init__(
    method compose (line 621) | def compose(self) -> ComposeResult:
    method update_selection (line 634) | def update_selection(self, event: TextArea.SelectionChanged) -> None:
    method update_visual_mode (line 638) | def update_visual_mode(self, event: ReadOnlyTextArea.VisualModeToggled...
    method update_language (line 642) | def update_language(self, event: TextAreaFooter.LanguageChanged) -> None:
    method update_soft_wrap (line 646) | def update_soft_wrap(self, event: TextAreaFooter.SoftWrapChanged) -> N...
    method text (line 650) | def text(self) -> str:
    method content_type (line 654) | def content_type(self) -> str | None:

FILE: src/posting/widgets/tree.py
  class PostingTree (line 9) | class PostingTree(Tree[T]):
    method action_cursor_up_parent (line 33) | def action_cursor_up_parent(self) -> None:
    method action_cursor_down_parent (line 42) | def action_cursor_down_parent(self) -> None:
    method walk_nodes (line 52) | def walk_nodes(self) -> Generator[TreeNode[T], None, None]:

FILE: src/posting/widgets/variable_autocomplete.py
  class VariableAutoComplete (line 18) | class VariableAutoComplete(AutoComplete):
    method __init__ (line 19) | def __init__(
    method get_candidates (line 51) | def get_candidates(self, target_state: TargetState) -> list[DropdownIt...
    method apply_completion (line 60) | def apply_completion(self, value: str, state: TargetState) -> None:
    method get_search_string (line 84) | def get_search_string(self, target_state: TargetState) -> str:
    method get_variable_candidates (line 93) | def get_variable_candidates(self, target_state: TargetState) -> list[D...

FILE: src/posting/widgets/variable_input.py
  class VariableInput (line 12) | class VariableInput(PostingInput):
    method __init__ (line 26) | def __init__(
    method on_mount (line 38) | def on_mount(self) -> None:
    method on_theme_change (line 47) | def on_theme_change(self, theme: Theme) -> None:
    method _get_variable_candidates (line 61) | def _get_variable_candidates(self, target_state: TargetState) -> list[...

FILE: src/posting/xresources.py
  function load_xresources_themes (line 20) | def load_xresources_themes() -> dict[str, TextualTheme]:

FILE: src/posting/yaml.py
  function str_presenter (line 10) | def str_presenter(dumper: Dumper, data: str) -> yaml.ScalarNode:

FILE: tests/sample-collections/scripts/my_script.py
  function setup (line 7) | def setup(posting: Posting) -> None:
  function on_request (line 13) | def on_request(request: RequestModel, posting: Posting) -> None:
  function on_response (line 27) | def on_response(response: httpx.Response, posting: Posting) -> None:

FILE: tests/test_curl_export.py
  function basic_request (line 15) | def basic_request():
  function test_simple_get_request (line 22) | def test_simple_get_request():
  function test_post_request_with_body (line 32) | def test_post_request_with_body():
  function test_request_with_headers (line 45) | def test_request_with_headers():
  function test_request_with_query_params (line 62) | def test_request_with_query_params():
  function test_request_with_existing_query_params (line 79) | def test_request_with_existing_query_params():
  function test_request_with_url_fragment (line 97) | def test_request_with_url_fragment():
  function test_request_with_basic_auth (line 110) | def test_request_with_basic_auth():
  function test_request_with_digest_auth (line 125) | def test_request_with_digest_auth():
  function test_request_with_cookies (line 138) | def test_request_with_cookies():
  function test_request_with_form_data (line 155) | def test_request_with_form_data():
  function test_request_with_options (line 174) | def test_request_with_options():
  function test_complex_request (line 192) | def test_complex_request():
  function test_special_characters_in_url_and_params (line 216) | def test_special_characters_in_url_and_params():

FILE: tests/test_curl_import.py
  function test_simple_get (line 4) | def test_simple_get():
  function test_get_with_headers (line 14) | def test_get_with_headers():
  function test_post_with_form_data (line 27) | def test_post_with_form_data():
  function test_post_with_json_data (line 38) | def test_post_with_json_data():
  function test_post_with_form_option (line 49) | def test_post_with_form_option():
  function test_multiple_data_options (line 61) | def test_multiple_data_options():
  function test_curl_with_user_and_password (line 71) | def test_curl_with_user_and_password():
  function test_curl_with_bearer_token (line 79) | def test_curl_with_bearer_token():
  function test_curl_with_insecure (line 87) | def test_curl_with_insecure():
  function test_curl_with_referer (line 94) | def test_curl_with_referer():
  function test_curl_with_user_agent (line 101) | def test_curl_with_user_agent():
  function test_curl_with_compressed (line 108) | def test_curl_with_compressed():
  function test_curl_with_method_and_data (line 115) | def test_curl_with_method_and_data():
  function test_curl_with_data_raw (line 124) | def test_curl_with_data_raw():
  function test_curl_with_data_binary (line 132) | def test_curl_with_data_binary():
  function test_curl_with_escaped_newlines (line 140) | def test_curl_with_escaped_newlines():
  function test_curl_with_no_space_in_header (line 153) | def test_curl_with_no_space_in_header():
  function test_curl_with_complex_command (line 160) | def test_curl_with_complex_command():
  function test_curl_with_utf8_characters (line 180) | def test_curl_with_utf8_characters():
  function test_curl_with_special_characters_in_data (line 188) | def test_curl_with_special_characters_in_data():
  function test_curl_imports_max_time (line 199) | def test_curl_imports_max_time():

FILE: tests/test_files.py
  function test_is_valid_filename (line 40) | def test_is_valid_filename(filename, expected):
  function test_is_valid_filename_with_none (line 44) | def test_is_valid_filename_with_none():
  function test_is_valid_filename_os_specific (line 57) | def test_is_valid_filename_os_specific(os_specific_filename, expected):

FILE: tests/test_open_api_import.py
  function test_import (line 7) | def test_import(tmp_path: Path):

FILE: tests/test_postman_import.py
  function test_import_postman_spec (line 6) | def test_import_postman_spec():

FILE: tests/test_snapshots.py
  function use_config (line 18) | def use_config(file_name: str):
  function patch_env (line 25) | def patch_env(key: str, value: str):
  function disable_blink_for_active_cursors (line 30) | async def disable_blink_for_active_cursors(pilot: Pilot) -> None:
  class TestJumpMode (line 40) | class TestJumpMode:
    method test_loads (line 42) | def test_loads(self, spacing, snap_compare):
    method test_focus_switch (line 51) | def test_focus_switch(self, snap_compare):
    method test_click_switch (line 60) | def test_click_switch(self, snap_compare):
  class TestMethodSelection (line 71) | class TestMethodSelection:
    method test_select_post_method (line 72) | def test_select_post_method(self, snap_compare):
  class TestUrlBar (line 84) | class TestUrlBar:
    method test_dropdown_appears_on_typing (line 85) | def test_dropdown_appears_on_typing(self, snap_compare):
    method test_dropdown_filters_on_typing (line 94) | def test_dropdown_filters_on_typing(self, snap_compare):
    method test_dropdown_completion_selected_via_enter_key (line 103) | def test_dropdown_completion_selected_via_enter_key(self, snap_compare):
    method test_dropdown_completion_selected_via_tab_key (line 113) | def test_dropdown_completion_selected_via_tab_key(self, snap_compare):
  class TestCommandPalette (line 125) | class TestCommandPalette:
    method test_loads_and_shows_discovery_options (line 127) | def test_loads_and_shows_discovery_options(self, spacing, snap_compare):
    method test_can_type_to_filter_options (line 139) | def test_can_type_to_filter_options(self, snap_compare):
    method test_can_run_command__hide_collection_browser (line 149) | def test_can_run_command__hide_collection_browser(self, snap_compare):
  class TestNewRequest (line 163) | class TestNewRequest:
    method test_dialog_loads_and_can_be_used (line 164) | def test_dialog_loads_and_can_be_used(self, snap_compare):
    method test_new_request_added_to_tree_correctly_and_notification_shown (line 177) | def test_new_request_added_to_tree_correctly_and_notification_shown(
    method test_cannot_create_request_without_name (line 211) | def test_cannot_create_request_without_name(self, snap_compare):
    method test_cannot_create_request_with_duplicate_name (line 224) | def test_cannot_create_request_with_duplicate_name(self, snap_compare):
    method test_cannot_create_request_invalid_filename (line 237) | def test_cannot_create_request_invalid_filename(self, snap_compare):
    method test_cannot_supply_invalid_path_in_collection (line 250) | def test_cannot_supply_invalid_path_in_collection(self, snap_compare):
  class TestUserInterfaceShortcuts (line 265) | class TestUserInterfaceShortcuts:
    method test_hide_collection_browser (line 266) | def test_hide_collection_browser(self, snap_compare):
    method test_expand_request_section (line 274) | def test_expand_request_section(self, snap_compare):
    method test_expand_then_reset (line 284) | def test_expand_then_reset(self, snap_compare):
  class TestLoadingRequest (line 298) | class TestLoadingRequest:
    method test_request_loaded_into_view__headers (line 299) | def test_request_loaded_into_view__headers(self, snap_compare):
    method test_request_loaded_into_view__body (line 309) | def test_request_loaded_into_view__body(self, snap_compare):
    method test_request_loaded_into_view__query_params (line 320) | def test_request_loaded_into_view__query_params(self, snap_compare):
    method test_request_loaded_into_view__auth (line 331) | def test_request_loaded_into_view__auth(self, snap_compare):
    method test_request_loaded_into_view__path_params (line 342) | def test_request_loaded_into_view__path_params(self, snap_compare):
    method test_request_loaded_into_view__options (line 365) | def test_request_loaded_into_view__options(self, snap_compare):
  class TestHelpScreen (line 377) | class TestHelpScreen:
    method test_help_screen_appears (line 378) | def test_help_screen_appears(self, snap_compare):
  class TestSave (line 389) | class TestSave:
    method test_no_request_selected__dialog_is_prefilled_correctly (line 390) | def test_no_request_selected__dialog_is_prefilled_correctly(self, snap...
  class TestSendRequest (line 412) | class TestSendRequest:
    method test_send_request (line 414) | def test_send_request(self, spacing, snap_compare):
  class TestConfig (line 430) | class TestConfig:
    method test_config (line 431) | def test_config(self, snap_compare):
  class TestVariables (line 454) | class TestVariables:
    method test_unresolved_variables_highlighted (line 455) | def test_unresolved_variables_highlighted(self, snap_compare):
    method test_resolved_variables_highlight_and_preview (line 471) | def test_resolved_variables_highlight_and_preview(self, spacing, snap_...
  class TestCustomThemeSimple (line 492) | class TestCustomThemeSimple:
    method test_theme_set_on_startup_and_in_command_palette (line 493) | def test_theme_set_on_startup_and_in_command_palette(self, snap_compare):
    method test_theme_sensible_defaults__url (line 505) | def test_theme_sensible_defaults__url(self, snap_compare):
    method test_theme_sensible_defaults__json (line 525) | def test_theme_sensible_defaults__json(self, snap_compare):
  class TestCustomThemeComplex (line 548) | class TestCustomThemeComplex:
    method test_highlighting_applied_from_custom_theme__url (line 549) | def test_highlighting_applied_from_custom_theme__url(self, snap_compare):
    method test_highlighting_applied_from_custom_theme__json (line 571) | def test_highlighting_applied_from_custom_theme__json(self, snap_compa...
  class TestFocusAutoSwitchingConfig (line 597) | class TestFocusAutoSwitchingConfig:
    method test_focus_on_request_open__open_body (line 608) | def test_focus_on_request_open__open_body(
  class TestDisableRowInTable (line 628) | class TestDisableRowInTable:
    method test_disable_row_in_table (line 629) | def test_disable_row_in_table(self, snap_compare):
  class TestCurlExport (line 645) | class TestCurlExport:
    method test_curl_export_no_setup (line 646) | def test_curl_export_no_setup(self, snap_compare):
    method test_curl_export (line 657) | def test_curl_export(self, snap_compare):
  class TestScripts (line 671) | class TestScripts:
    method test_script_runs (line 672) | def test_script_runs(self, snap_compare):
  class TestHeaderAutoCompletion (line 687) | class TestHeaderAutoCompletion:
    method test_header_name_auto_completion_list_appears (line 688) | def test_header_name_auto_completion_list_appears(self, snap_compare):
    method test_header_name_auto_completion_list_appears_followed_by_keypress (line 697) | def test_header_name_auto_completion_list_appears_followed_by_keypress(
    method test_header_value_auto_completion_list_appears (line 712) | def test_header_value_auto_completion_list_appears(self, snap_compare):
    method test_header_value_auto_completion_list_accepts_selection (line 721) | def test_header_value_auto_completion_list_accepts_selection(self, sna...
  class TestEditKeyValues (line 737) | class TestEditKeyValues:
    method test_edit_mode_displays_correctly (line 738) | def test_edit_mode_displays_correctly(self, snap_compare):
    method test_edit_mode_can_edit_header_keys_and_values_as_expected (line 752) | def test_edit_mode_can_edit_header_keys_and_values_as_expected(self, s...

FILE: tests/test_urls.py
  function test_ensure_protocol (line 8) | def test_ensure_protocol():
  function test_extract_path_param_names_with_escaping (line 23) | def test_extract_path_param_names_with_escaping():
  function test_substitute_path_params_with_escaping_simple (line 32) | def test_substitute_path_params_with_escaping_simple():
  function test_substitute_path_params_preserves_escaped_literal (line 38) | def test_substitute_path_params_preserves_escaped_literal():
  function test_substitute_path_params_mixed_escaped_and_real (line 45) | def test_substitute_path_params_mixed_escaped_and_real():

FILE: tests/test_variables.py
  function test_find_variables (line 25) | def test_find_variables(text: str, expected: list[tuple[str, int, int]]):
  function test_variable_range_at_cursor (line 72) | def test_variable_range_at_cursor(
Condensed preview — 141 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,294K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 879,
    "preview": "# These are supported funding model platforms\n\ngithub: [darrenburns] # Replace with up to 4 GitHub Sponsors-enabled user"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "chars": 924,
    "preview": "# This is a follow up job that runs after the CI job has completed.\n# It'll post a code coverage comment on pull request"
  },
  {
    "path": ".github/workflows/docs.yml",
    "chars": 761,
    "preview": "name: docs\non:\n  push:\n    branches:\n      - main\npermissions:\n  contents: write\njobs:\n  deploy:\n    runs-on: ubuntu-lat"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1442,
    "preview": "name: Continuous Integration\n\non:\n    pull_request:\n    push:\n      branches:\n        - \"main\"\n\nenv:\n    PYTEST_ADDOPTS:"
  },
  {
    "path": ".gitignore",
    "chars": 204,
    "preview": "# python generated files\n__pycache__/\n*.py[oc]\nbuild/\ndist/\nwheels/\n*.egg-info\n\n# venv\n.venv\n\n# editor\n.vscode/\n.idea/\np"
  },
  {
    "path": ".python-version",
    "chars": 7,
    "preview": "3.11.7\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3626,
    "preview": "# Contributing to Posting\n\nPosting is open to contributions! 🚀\n\nContributions of any type, no matter the \"size\" and form"
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Makefile",
    "chars": 523,
    "preview": "\n.PHONY: test\ntest:\n\t$(run) pytest --cov=posting tests/ -n 24 -m \"not serial\" $(ARGS)\n\t$(run) pytest --cov-report term-m"
  },
  {
    "path": "NOTICE",
    "chars": 1619,
    "preview": "This product includes software developed by [Encode OSS Ltd.](https://github.com/encode), under the BSD-3-Clause License"
  },
  {
    "path": "README.md",
    "chars": 1749,
    "preview": "# Posting\n\n**A powerful HTTP client that lives in your terminal.**\n\nPosting is an HTTP client, not unlike Postman and In"
  },
  {
    "path": "docs/CHANGELOG.md",
    "chars": 17115,
    "preview": "## 2.9.2 [14th October 2025]\n\n### Fixed\n\n- Fixed path parameters interference across requests.\n\n## 2.9.1 [24th September"
  },
  {
    "path": "docs/CNAME",
    "chars": 10,
    "preview": "POSTING.SH"
  },
  {
    "path": "docs/faq.md",
    "chars": 1223,
    "preview": "# Frequently Asked Questions\n\n## Using Posting\n\n### How do I edit headers or query parameters?\n\nRight now, you need to d"
  },
  {
    "path": "docs/guide/collections.md",
    "chars": 3249,
    "preview": "## Overview\n\nA *collection* is just a directory on your file system which may or may not contain requests in the `.posti"
  },
  {
    "path": "docs/guide/command_palette.md",
    "chars": 321,
    "preview": "## Overview\n\nThe *command palette* is a way to search for and execute commands in Posting.\n\nSome functionality in Postin"
  },
  {
    "path": "docs/guide/configuration.md",
    "chars": 9345,
    "preview": "## Overview\n\nPosting can be configured using a configuration file, environment variables, and/or `.env` files.\n\nConfigur"
  },
  {
    "path": "docs/guide/environments.md",
    "chars": 3582,
    "preview": "## Overview\n\nYou can use *variables* in input fields and text areas using the `${VARIABLE_NAME}` or `$VARIABLE_NAME` syn"
  },
  {
    "path": "docs/guide/external_tools.md",
    "chars": 2413,
    "preview": "## Overview\n\nYou can quickly switch between Posting and external editors and pagers.\n\nFor example, you could edit reques"
  },
  {
    "path": "docs/guide/help_system.md",
    "chars": 591,
    "preview": "## Overview\n\nPosting has a *built-in help system*, which can be used to get information about the currently focused widg"
  },
  {
    "path": "docs/guide/importing.md",
    "chars": 1350,
    "preview": "## Overview\n\nPosting supports importing from external sources.\n\n## Importing from curl\n\n!!! example \"This feature is exp"
  },
  {
    "path": "docs/guide/index.md",
    "chars": 9032,
    "preview": "Posting can be installed in a matter of seconds on MacOS, Linux, and Windows.\n\n## Installation\n\nThe recommended method i"
  },
  {
    "path": "docs/guide/keymap.md",
    "chars": 4656,
    "preview": "## Overview\n\nAs explained in the [Help System](./help_system.md) section, you can view the keybindings for any widget by"
  },
  {
    "path": "docs/guide/navigation.md",
    "chars": 3027,
    "preview": "Posting can be navigated using either mouse or keyboard.\n\n## Jump mode\n\nJump mode is the fastest way to get around.\n\nPre"
  },
  {
    "path": "docs/guide/requests.md",
    "chars": 4599,
    "preview": "## Overview\n\nRequests are stored directly on your file system as simple YAML files, suffixed with `.posting.yaml` - easy"
  },
  {
    "path": "docs/guide/scripting.md",
    "chars": 7014,
    "preview": "## Overview\n\nYou can attach simple Python scripts to requests inside the `Scripts` tab, and have Posting run them at var"
  },
  {
    "path": "docs/guide/themes.md",
    "chars": 4068,
    "preview": "## Overview\n\nPosting ships with several built-in themes, and also supports custom, user-made themes.\n\nWhen editing a the"
  },
  {
    "path": "docs/index.md",
    "chars": 78,
    "preview": "---\ntitle: The API client that lives in your terminal\ntemplate: home.html\n---\n"
  },
  {
    "path": "docs/overrides/home.html",
    "chars": 151810,
    "preview": "{% extends \"main.html\" %}\n{% block tabs %}\n{{ super() }}\n<style>\n    /* hide the main content on the home page.\n    we'r"
  },
  {
    "path": "docs/roadmap.md",
    "chars": 7969,
    "preview": "## About this document\n\nIf you have any feedback or suggestions, please open a [new discussion on GitHub](https://github"
  },
  {
    "path": "docs/stylesheets/extra.css",
    "chars": 3874,
    "preview": "h1, h2, h3, h4, h5, h6 {\n    font-family: \"Roboto Mono\", monospace;\n    font-weight: 700;\n    font-style: normal;\n    fo"
  },
  {
    "path": "mkdocs.yml",
    "chars": 1763,
    "preview": "site_name: Posting\nsite_url: https://posting.sh\nrepo_url: https://github.com/darrenburns/posting\nrepo_name: darrenburns/"
  },
  {
    "path": "pyproject.toml",
    "chars": 2157,
    "preview": "[project]\nname = \"posting\"\nversion = \"2.9.2\"\ndescription = \"The modern API client that lives in your terminal.\"\nauthors "
  },
  {
    "path": "src/posting/__init__.py",
    "chars": 647,
    "preview": "# This import should be the first thing to run, to ensure that\n# the START_TIME is set as early as possible.\nfrom postin"
  },
  {
    "path": "src/posting/__main__.py",
    "chars": 6531,
    "preview": "\"\"\"The main entry point for the Posting CLI.\"\"\"\n\nfrom pathlib import Path\nimport click\nimport os\n\nfrom click_default_gro"
  },
  {
    "path": "src/posting/_start_time.py",
    "chars": 50,
    "preview": "import time\n\n\nSTART_TIME = time.perf_counter_ns()\n"
  },
  {
    "path": "src/posting/app.py",
    "chars": 65648,
    "preview": "import inspect\nfrom contextlib import redirect_stdout, redirect_stderr\nimport os\nfrom pathlib import Path\nimport sys\nfro"
  },
  {
    "path": "src/posting/auth.py",
    "chars": 336,
    "preview": "from typing import Generator\n\nimport httpx\n\n\nclass HttpxBearerTokenAuth(httpx.Auth):\n    def __init__(self, token: str):"
  },
  {
    "path": "src/posting/collection.py",
    "chars": 19866,
    "preview": "from __future__ import annotations\nfrom functools import total_ordering\nfrom urllib.parse import urlparse, parse_qsl, ur"
  },
  {
    "path": "src/posting/commands.py",
    "chars": 8173,
    "preview": "from functools import partial\nfrom typing import TYPE_CHECKING, cast\nfrom textual.command import DiscoveryHit, Hit, Hits"
  },
  {
    "path": "src/posting/config.py",
    "chars": 9092,
    "preview": "from contextvars import ContextVar\nimport os\nfrom pathlib import Path\nfrom typing import Literal, Type\nfrom pydantic imp"
  },
  {
    "path": "src/posting/exit_codes.py",
    "chars": 18,
    "preview": "GENERAL_ERROR = 1\n"
  },
  {
    "path": "src/posting/files.py",
    "chars": 4247,
    "preview": "from pathlib import Path\n\nfrom posting.save_request import FILE_SUFFIX\n\n\nimport os\nimport re\n\n\ndef is_valid_filename(fil"
  },
  {
    "path": "src/posting/help_data.py",
    "chars": 314,
    "preview": "from dataclasses import dataclass, field\n\n\n@dataclass\nclass HelpData:\n    \"\"\"Data relating to the widget to be displayed"
  },
  {
    "path": "src/posting/help_screen.py",
    "chars": 5612,
    "preview": "from typing import Protocol, runtime_checkable\nfrom rich.text import Text\nfrom textual.app import ComposeResult\nfrom tex"
  },
  {
    "path": "src/posting/highlight_url.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/posting/highlighters.py",
    "chars": 4172,
    "preview": "import re\nfrom rich.highlighter import Highlighter\nfrom rich.style import Style\nfrom rich.text import Text\nfrom textual."
  },
  {
    "path": "src/posting/importing/curl.py",
    "chars": 11808,
    "preview": "import argparse\nimport base64\nfrom datetime import datetime\nimport shlex\nfrom typing import cast\nfrom urllib.parse impor"
  },
  {
    "path": "src/posting/importing/open_api.py",
    "chars": 13977,
    "preview": "from __future__ import annotations\nimport re\nfrom typing import Any\nfrom urllib.parse import urlparse\nimport json\n\nimpor"
  },
  {
    "path": "src/posting/importing/postman.py",
    "chars": 7104,
    "preview": "from __future__ import annotations\n\nfrom pathlib import Path\nimport json\nimport re\nfrom urllib.parse import urlparse, ur"
  },
  {
    "path": "src/posting/jump_overlay.py",
    "chars": 4081,
    "preview": "from typing import TYPE_CHECKING\nfrom textual import events, log\nfrom textual.app import ComposeResult\nfrom textual.bind"
  },
  {
    "path": "src/posting/jumper.py",
    "chars": 1908,
    "preview": "from typing import Any, Mapping, NamedTuple, Protocol, runtime_checkable\nfrom textual.errors import NoWidget\n\nfrom textu"
  },
  {
    "path": "src/posting/locations.py",
    "chars": 972,
    "preview": "from pathlib import Path\n\nfrom xdg_base_dirs import xdg_config_home, xdg_data_home\n\n\ndef _posting_directory(root: Path) "
  },
  {
    "path": "src/posting/messages.py",
    "chars": 170,
    "preview": "from dataclasses import dataclass\nfrom httpx import Response\nfrom textual.message import Message\n\n\n@dataclass\nclass Http"
  },
  {
    "path": "src/posting/posting.scss",
    "chars": 14471,
    "preview": "$empty-hatch: right $surface-lighten-1 70%;\n\n* {\n  scrollbar-color: $primary 10%;\n  scrollbar-color-hover: $primary 80%;"
  },
  {
    "path": "src/posting/request_headers.py",
    "chars": 17684,
    "preview": "from typing import TypedDict\n\n\nclass RequestHeader(TypedDict):\n    section: str\n    name: str\n    description: str\n    e"
  },
  {
    "path": "src/posting/save_request.py",
    "chars": 403,
    "preview": "from __future__ import annotations\nimport re\n\n\nFILE_SUFFIX = \".posting.yaml\"\n\n\ndef slugify(text: str) -> str:\n    \"\"\"Slu"
  },
  {
    "path": "src/posting/scripts.py",
    "chars": 6769,
    "preview": "from __future__ import annotations\n\nimport sys\nfrom pathlib import Path\nfrom types import ModuleType\nfrom typing import "
  },
  {
    "path": "src/posting/suggesters.py",
    "chars": 392,
    "preview": "# Maps lowercased HTTP headers to lists of suggestions for the header's value.\nHEADER_SUGGESTION_LISTS = {\n    \"content-"
  },
  {
    "path": "src/posting/themes.py",
    "chars": 20468,
    "preview": "from pathlib import Path\nfrom typing import NamedTuple\nimport uuid\nfrom pydantic import BaseModel, Field\nfrom rich.style"
  },
  {
    "path": "src/posting/tuple_to_multidict.py",
    "chars": 341,
    "preview": "from __future__ import annotations\nfrom collections import defaultdict\nfrom typing import TypeVar\n\nK = TypeVar(\"K\")\nV = "
  },
  {
    "path": "src/posting/types.py",
    "chars": 326,
    "preview": "from typing import Literal, Optional, Tuple, Union\n\nPostingLayout = Literal[\"horizontal\", \"vertical\"]\n\n# From httpx - se"
  },
  {
    "path": "src/posting/urls.py",
    "chars": 2744,
    "preview": "import re\nfrom urllib.parse import urlparse, urlunparse\n\n\n# Match a single-colon path param like \":id\", but ignore escap"
  },
  {
    "path": "src/posting/user_host.py",
    "chars": 372,
    "preview": "import getpass\nimport socket\n\nfrom rich.text import Text\n\nfrom posting.config import SETTINGS\n\n\ndef get_user_host_string"
  },
  {
    "path": "src/posting/variables.py",
    "chars": 4760,
    "preview": "from __future__ import annotations\nfrom functools import lru_cache\n\nimport re\nimport os\nfrom pathlib import Path\nfrom do"
  },
  {
    "path": "src/posting/version.py",
    "chars": 69,
    "preview": "from importlib.metadata import version\n\nVERSION = version(\"posting\")\n"
  },
  {
    "path": "src/posting/widgets/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/posting/widgets/center_middle.py",
    "chars": 273,
    "preview": "from textual.widget import Widget\n\n\nclass CenterMiddle(Widget, inherit_bindings=False):\n    \"\"\"A container which aligns "
  },
  {
    "path": "src/posting/widgets/collection/browser.py",
    "chars": 24215,
    "preview": "import bisect\nfrom dataclasses import dataclass\nfrom functools import partial\nimport os\nfrom pathlib import Path\nfrom ty"
  },
  {
    "path": "src/posting/widgets/collection/new_request_modal.py",
    "chars": 10495,
    "preview": "from dataclasses import dataclass\nfrom typing import TYPE_CHECKING\nfrom textual import on\nfrom textual.app import Compos"
  },
  {
    "path": "src/posting/widgets/confirmation.py",
    "chars": 2665,
    "preview": "\"\"\"A modal screen for confirming a destructive action.\"\"\"\n\nfrom typing import Literal\nfrom textual import on\nfrom textua"
  },
  {
    "path": "src/posting/widgets/datatable.py",
    "chars": 9580,
    "preview": "from dataclasses import dataclass, field\nfrom typing import Any, Iterable, Self\nfrom rich.style import Style\nfrom rich.t"
  },
  {
    "path": "src/posting/widgets/input.py",
    "chars": 904,
    "preview": "from rich.style import Style\nfrom textual.theme import Theme\nfrom textual.widgets import Input\n\n\nfrom posting.config imp"
  },
  {
    "path": "src/posting/widgets/key_value.py",
    "chars": 12557,
    "preview": "from dataclasses import dataclass\nfrom rich.style import Style\nfrom rich.text import Text\nfrom textual import on, log\nfr"
  },
  {
    "path": "src/posting/widgets/request/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/posting/widgets/request/form_editor.py",
    "chars": 2006,
    "preview": "from typing import Iterable\nfrom rich.text import Text\nfrom textual.app import ComposeResult\nfrom textual.binding import"
  },
  {
    "path": "src/posting/widgets/request/header_editor.py",
    "chars": 8173,
    "preview": "from rich.text import Text\nfrom textual.app import ComposeResult\nfrom textual.binding import Binding\nfrom textual.contai"
  },
  {
    "path": "src/posting/widgets/request/method_selection.py",
    "chars": 2676,
    "preview": "from dataclasses import dataclass\nfrom rich.console import RenderableType\n\nfrom textual import on\nfrom textual.binding i"
  },
  {
    "path": "src/posting/widgets/request/path_editor.py",
    "chars": 6219,
    "preview": "from dataclasses import dataclass\nfrom textual import on\nfrom textual.binding import Binding\nfrom rich.text import Text\n"
  },
  {
    "path": "src/posting/widgets/request/query_editor.py",
    "chars": 2091,
    "preview": "from rich.text import Text\nfrom textual.app import ComposeResult\nfrom textual.binding import Binding\nfrom textual.contai"
  },
  {
    "path": "src/posting/widgets/request/request_auth.py",
    "chars": 8936,
    "preview": "from typing import Protocol, runtime_checkable\nimport httpx\nfrom textual import on, log\nfrom textual.app import ComposeR"
  },
  {
    "path": "src/posting/widgets/request/request_body.py",
    "chars": 2287,
    "preview": "from textual.app import ComposeResult\nfrom textual.containers import Horizontal, Vertical\nfrom textual.widgets import Co"
  },
  {
    "path": "src/posting/widgets/request/request_editor.py",
    "chars": 4521,
    "preview": "from typing import TYPE_CHECKING, Any, cast\nfrom rich.text import Text\nfrom textual import on, log\nfrom textual.css.quer"
  },
  {
    "path": "src/posting/widgets/request/request_metadata.py",
    "chars": 2164,
    "preview": "from textual.app import ComposeResult\nfrom textual.containers import VerticalScroll\nfrom textual.reactive import Reactiv"
  },
  {
    "path": "src/posting/widgets/request/request_options.py",
    "chars": 5903,
    "preview": "from textual import on\nfrom textual.app import ComposeResult\nfrom textual.binding import Binding\nfrom textual.containers"
  },
  {
    "path": "src/posting/widgets/request/request_scripts.py",
    "chars": 8197,
    "preview": "from pathlib import Path\nimport shlex\nimport subprocess\nfrom typing import Any\nfrom textual.app import ComposeResult\nfro"
  },
  {
    "path": "src/posting/widgets/request/url_bar.py",
    "chars": 12392,
    "preview": "from dataclasses import dataclass\nimport re\nfrom typing import Any\nfrom rich.text import Text\nfrom textual import on\nfro"
  },
  {
    "path": "src/posting/widgets/response/cookies_table.py",
    "chars": 1187,
    "preview": "from textual import on\nfrom textual.app import ComposeResult\nfrom textual.containers import Vertical\nfrom textual.widget"
  },
  {
    "path": "src/posting/widgets/response/response_area.py",
    "chars": 5992,
    "preview": "import json\nimport httpx\nfrom textual.lazy import Lazy\nfrom posting.config import SETTINGS\n\nfrom posting.widgets.respons"
  },
  {
    "path": "src/posting/widgets/response/response_body.py",
    "chars": 1108,
    "preview": "from textual import on\nfrom textual.widgets import TextArea\nfrom posting.help_data import HelpData\n\nfrom posting.widgets"
  },
  {
    "path": "src/posting/widgets/response/response_headers.py",
    "chars": 357,
    "preview": "from posting.widgets.datatable import PostingDataTable\n\n\nclass ResponseHeadersTable(PostingDataTable):\n    def on_mount("
  },
  {
    "path": "src/posting/widgets/response/response_trace.py",
    "chars": 2918,
    "preview": "import time\nfrom typing import Any, Literal\nfrom textual.app import ComposeResult\nfrom textual.containers import Vertica"
  },
  {
    "path": "src/posting/widgets/response/script_output.py",
    "chars": 3571,
    "preview": "\"\"\"Tab for displaying the output of a script.\nhttps://github.com/sergeyklay/gohugo-theme-ed/blob/main/exampleSite/hugo.t"
  },
  {
    "path": "src/posting/widgets/rich_log.py",
    "chars": 1349,
    "preview": "from io import StringIO\nfrom typing import Literal\nfrom textual.binding import Binding\nfrom textual.widgets import RichL"
  },
  {
    "path": "src/posting/widgets/select.py",
    "chars": 894,
    "preview": "from typing import TypeVar\nfrom textual.binding import Binding\nfrom textual.widgets import Select\nfrom textual.widgets._"
  },
  {
    "path": "src/posting/widgets/tabbed_content.py",
    "chars": 708,
    "preview": "from textual.binding import Binding\nfrom textual.widgets import TabbedContent, Tabs\n\n\nclass PostingTabbedContent(TabbedC"
  },
  {
    "path": "src/posting/widgets/text_area.py",
    "chars": 25033,
    "preview": "import os\nimport shlex\nimport subprocess\nimport tempfile\nfrom dataclasses import dataclass\nfrom typing import Iterable\n\n"
  },
  {
    "path": "src/posting/widgets/tree.py",
    "chars": 1975,
    "preview": "from typing import Generator, TypeVar\nfrom textual.binding import Binding\nfrom textual.widgets import Tree\nfrom textual."
  },
  {
    "path": "src/posting/widgets/variable_autocomplete.py",
    "chars": 3269,
    "preview": "from typing import Callable, Sequence\nfrom textual.widgets import Input\nfrom textual_autocomplete import (\n    AutoCompl"
  },
  {
    "path": "src/posting/widgets/variable_input.py",
    "chars": 2222,
    "preview": "from typing import Any, Callable\nfrom textual_autocomplete import DropdownItem, TargetState\nfrom posting.help_data impor"
  },
  {
    "path": "src/posting/xresources.py",
    "chars": 1748,
    "preview": "import itertools\nimport subprocess\nfrom typing import Any\n\nfrom posting.themes import Theme\nfrom textual.theme import Th"
  },
  {
    "path": "src/posting/yaml.py",
    "chars": 720,
    "preview": "from yaml import load, dump\nimport yaml\n\ntry:\n    from yaml import CLoader as Loader, Dumper as Dumper\nexcept ImportErro"
  },
  {
    "path": "tests/posting_snapshot_app.py",
    "chars": 333,
    "preview": "from pathlib import Path\nfrom posting.__main__ import make_posting\n\nCOLLECTION = Path(__file__).parent / \"sample-collect"
  },
  {
    "path": "tests/resources/snapshot_report_template.jinja2",
    "chars": 11777,
    "preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "tests/sample-collections/echo-post-01.posting.yaml",
    "chars": 633,
    "preview": "name: echo post\ndescription: Echo server for post requests.\nmethod: POST\nurl: https://postman-echo.com/post\nbody:\n  cont"
  },
  {
    "path": "tests/sample-collections/echo.posting.yaml",
    "chars": 387,
    "preview": "name: echo\ndescription: This is an echo server we can use to see exactly what request is being\n  sent.\nurl: https://post"
  },
  {
    "path": "tests/sample-collections/get-random-user.posting.yaml",
    "chars": 53,
    "preview": "name: get random user\nurl: https://api.randomuser.me\n"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/comments/edit.posting.yaml",
    "chars": 516,
    "preview": "name: edit a comment\ndescription: Edit a comment\nmethod: PUT\nurl: https://jsonplaceholder.typicode.com/comments/1\nbody: "
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/comments/get-comments-query.posting.yaml",
    "chars": 511,
    "preview": "name: get comments (via param)\ndescription: Retrieve the comments for a post using the query string\nurl: https://jsonpla"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/comments/get-comments.posting.yaml",
    "chars": 351,
    "preview": "name: get comments\ndescription: Retrieve the comments for a post\nurl: https://jsonplaceholder.typicode.com/posts/:postId"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/create.posting.yaml",
    "chars": 402,
    "preview": "name: create\ndescription: Create a new post\nmethod: POST\nurl: https://jsonplaceholder.typicode.com/posts\nbody:\n  content"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/delete.posting.yaml",
    "chars": 299,
    "preview": "name: delete a post\ndescription: Delete a single post\nmethod: DELETE\nurl: https://jsonplaceholder.typicode.com/posts/1\nh"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/get-all.posting.yaml",
    "chars": 274,
    "preview": "name: get all\ndescription: Retrieve all posts\nurl: https://jsonplaceholder.typicode.com/posts\nheaders:\n- name: Content-T"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/posts/get-one.posting.yaml",
    "chars": 283,
    "preview": "name: get one\ndescription: Retrieve one post\nurl: https://jsonplaceholder.typicode.com/posts/$POST_ID/\nheaders:\n- name: "
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/todos/get-all.posting.yaml",
    "chars": 309,
    "preview": "name: get all\ndescription: Retrieve all todos\nurl: https://jsonplaceholder.typicode.com/todos\nheaders:\n- name: Content-T"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/todos/get-one.posting.yaml",
    "chars": 317,
    "preview": "name: get one\ndescription: Retrieve one todo\nurl: https://jsonplaceholder.typicode.com/todos/$TODO_ID\nheaders:\n- name: C"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/users/create.posting.yaml",
    "chars": 530,
    "preview": "name: create a user\ndescription: Create a new user\nmethod: POST\nurl: https://jsonplaceholder.typicode.com/users\nbody:\n  "
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/users/delete.posting.yaml",
    "chars": 327,
    "preview": "name: delete a user\ndescription: Delete a user\nmethod: DELETE\nurl: https://jsonplaceholder.typicode.com/users/1\nheaders:"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/users/get-all.posting.yaml",
    "chars": 315,
    "preview": "name: get all users\ndescription: Retrieve all users\nurl: https://jsonplaceholder.typicode.com/users\nheaders:\n- name: Con"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/users/get-one.posting.yaml",
    "chars": 355,
    "preview": "name: get a user\ndescription: Retrieve a single user\nurl: https://jsonplaceholder.typicode.com/users/${USER_ID_UNRESOLVE"
  },
  {
    "path": "tests/sample-collections/jsonplaceholder/users/update.posting.yaml",
    "chars": 453,
    "preview": "name: update a user\ndescription: Update a user\nmethod: PUT\nurl: https://jsonplaceholder.typicode.com/users/1\nbody:\n  con"
  },
  {
    "path": "tests/sample-collections/scripts/my_script.py",
    "chars": 1080,
    "preview": "import sys\nimport httpx\n\nfrom posting import Auth, Header, RequestModel, Posting\n\n\ndef setup(posting: Posting) -> None:\n"
  },
  {
    "path": "tests/sample-configs/custom_theme.yaml",
    "chars": 322,
    "preview": "# Use a user-defined theme from the themes dir.\ntheme: serene_ocean  # corresponds to two.yaml\nuse_host_environment: fal"
  },
  {
    "path": "tests/sample-configs/custom_theme2.yaml",
    "chars": 292,
    "preview": "# Use a user-defined theme from the themes dir.\ntheme: anothertest\nuse_host_environment: false\nresponse:\n  show_size_and"
  },
  {
    "path": "tests/sample-configs/general.yaml",
    "chars": 249,
    "preview": "use_host_environment: false\nload_user_themes: false\nresponse:\n  show_size_and_time: false\nheading:\n  show_host: false\n  "
  },
  {
    "path": "tests/sample-configs/modified_config.yaml",
    "chars": 310,
    "preview": "theme: galaxy\nlayout: horizontal\nuse_host_environment: false\nload_user_themes: false\nresponse:\n  show_size_and_time: fal"
  },
  {
    "path": "tests/sample-envs/sample_base.env",
    "chars": 56,
    "preview": "POST_ID=1\nUSER_ID=2\nTODO_ID=1\nFILE=\"base\"\nONLY_BASE=true"
  },
  {
    "path": "tests/sample-envs/sample_extra.env",
    "chars": 38,
    "preview": "FILE=\"extra\"\nPOST_ID=2\nONLY_EXTRA=true"
  },
  {
    "path": "tests/sample-importable-collections/Fixer.postman_collection.json",
    "chars": 52846,
    "preview": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"0d9c66b3-5f1a-42ed-a5ca-379217bd629d\",\n\t\t\"name\": \"Fixer\",\n\t\t\"description\": \"Powered by 15"
  },
  {
    "path": "tests/sample-importable-collections/postman_collection.json",
    "chars": 2484758,
    "preview": "{\n\t\"info\": {\n\t\t\"_postman_id\": \"3ae89321-fd45-4694-880c-3081da4ef678\",\n\t\t\"name\": \"Fastly API\",\n\t\t\"description\": \"Via the "
  },
  {
    "path": "tests/sample-importable-collections/test-postman-collection.json",
    "chars": 1788,
    "preview": "{\n    \"info\": {\n        \"name\": \"Test API\",\n        \"description\": \"A test API\",\n        \"schema\": \"https://schema.getpo"
  },
  {
    "path": "tests/sample-themes/another_test.yml",
    "chars": 873,
    "preview": "name: anothertest\nprimary: '#2ecc71'\nsecondary: '#3498db'\naccent: '#9b59b6'\nbackground: '#ecf0f1'\nsurface: '#bdc3c7'\nerr"
  },
  {
    "path": "tests/sample-themes/serene_ocean.yaml",
    "chars": 293,
    "preview": "name: serene_ocean\nprimary: '#1E88E5'  # Ocean Blue\nsecondary: '#00ACC1'  # Teal\naccent: '#D32F2F'  # Crimson Red\nbackgr"
  },
  {
    "path": "tests/test_curl_export.py",
    "chars": 7780,
    "preview": "import pytest\nfrom posting.collection import (\n    RequestModel,\n    Header,\n    QueryParam,\n    Auth,\n    FormItem,\n   "
  },
  {
    "path": "tests/test_curl_import.py",
    "chars": 8515,
    "preview": "from posting.importing.curl import CurlImport\n\n\ndef test_simple_get():\n    \"\"\"Test a simple GET request.\"\"\"\n    curl_com"
  },
  {
    "path": "tests/test_files.py",
    "chars": 1753,
    "preview": "import pytest\nfrom posting.files import is_valid_filename\n\n\n@pytest.mark.parametrize(\n    \"filename, expected\",\n    [\n  "
  },
  {
    "path": "tests/test_open_api_import.py",
    "chars": 1990,
    "preview": "import json\nfrom pathlib import Path\n\nfrom posting.importing.open_api import import_openapi_spec\n\n\ndef test_import(tmp_p"
  },
  {
    "path": "tests/test_postman_import.py",
    "chars": 3888,
    "preview": "from pathlib import Path\nfrom posting.collection import Collection, Options, QueryParam, RequestModel, Scripts\nfrom post"
  },
  {
    "path": "tests/test_snapshots.py",
    "chars": 29152,
    "preview": "import os\nfrom pathlib import Path\nfrom unittest import mock\nimport pytest\n\nfrom textual.pilot import Pilot\nfrom textual"
  },
  {
    "path": "tests/test_urls.py",
    "chars": 1876,
    "preview": "from posting.urls import (\n    ensure_protocol,\n    extract_path_param_names,\n    substitute_path_params,\n)\n\n\ndef test_e"
  },
  {
    "path": "tests/test_variables.py",
    "chars": 2423,
    "preview": "import pytest\nfrom posting.variables import find_variables, variable_range_at_cursor\n\n\n@pytest.mark.parametrize(\n    \"te"
  }
]

About this extraction

This page contains the full source code of the darrenburns/posting GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 141 files (3.1 MB), approximately 830.2k tokens, and a symbol index with 792 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!