Skip to content

Links do not open properly using App.open_url() or Link when running in web (textual-serve), but works as local application. #6245

@MrMaelu

Description

@MrMaelu

Versions

Textual 6.6.0
Textual-serve 1.1.3

The bug

When I click a Link object in a textual app or use the App.open_path() method while running in web the link opens only partially.

  • Running as a local application does not cause this issue, it works fine.
  • Using webbrowser() works in both cases, but will open the link on the server side obviously.
  • Using unquote or quote to manage the links does not affect the outcome.

I suspect the issue is with links that have spaces and/or special characters to resolve, as links without any of that works.

The provided code will reproduce the issue easily.
The 4 first links will all show the issue, link 5 works in all cases.

Code example to reproduce:

from textual.app import App, ComposeResult
from textual.containers import Horizontal, Vertical
from textual.widgets import Button, Link, Header

sample_links = [
    "https://myrient.erista.me/files/No-Intro/Commodore%20-%20Commodore%2064/128er%20Quickload%20%28USA%2C%20Europe%29%20%28Program%29.zip",
    "https://myrient.erista.me/files/No-Intro/Commodore%20-%20Commodore%2064/256K-EPROM-System%20%28Germany%29%20%28v5%29%20%28Program%29.zip",
    "https://myrient.erista.me/files/No-Intro/Commodore%20-%20Commodore%2064/64%20Doctor%20%28USA%2C%20Europe%29%20%28Program%29.zip",
    "https://myrient.erista.me/files/No-Intro/Commodore%20-%20Commodore%2064/Action%20Replay%20Professional%20%28Europe%29%20%28v4.2%29%20%28Program%29%20%28Unl%29.zip",
    "http://ftp.uninett.no/linux/ubuntu-iso/25.04/ubuntu-25.04-desktop-amd64.iso",
    ]


class TUI(App):
    def compose(self) -> ComposeResult:
        yield Header("Demo app", id="header")
        yield Vertical(
                Link(sample_links[0]),
                Link(sample_links[1]),
                Link(sample_links[2]),
                Link(sample_links[3]),
                Link(sample_links[4]),
                Button("Download link 1", id="download_1", variant="primary"),
                Button("Download link 2", id="download_2", variant="primary"),
                Button("Download link 3", id="download_3", variant="primary"),
                Button("Download link 4", id="download_4", variant="primary"),
                Button("Download link 5", id="download_5", variant="primary"),
                )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "download_1":
            self.open_url(sample_links[0])
        if event.button.id == "download_2":
            self.open_url(sample_links[1])
        if event.button.id == "download_3":
            self.open_url(sample_links[2])
        if event.button.id == "download_4":
            self.open_url(sample_links[3])
        if event.button.id == "download_5":
            self.open_url(sample_links[4])


if __name__ == "__main__":
    app = TUI()
    app.run()

Textual Diagnostics

Versions

Name Value
Textual 6.6.0
Rich 14.2.0

Python

Name Value
Version 3.12.2
Implementation CPython
Compiler MSC v.1937 64 bit (AMD64)
Executable C:\Program Files\Python312\python.exe

Operating System

Name Value
System Windows
Release 11
Version 10.0.22621

Terminal

Name Value
Terminal Application vscode (1.106.2)
TERM Not set
COLORTERM truecolor
FORCE_COLOR Not set
NO_COLOR Not set

Rich Console options

Name Value
size width=311, height=34
legacy_windows False
min_width 1
max_width 311
is_terminal True
encoding utf-8
max_height 34
justify None
overflow None
no_wrap False
highlight None
markup None
height None

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions