From 154e6aabd3075280b5f30c0b1a6bddf074e434cb Mon Sep 17 00:00:00 2001 From: Dave Pearson Date: Mon, 10 Feb 2025 22:19:03 +0000 Subject: [PATCH] :sparkles: Add table of content support Also tidy up how I work around https://github.com/Textualize/textual/issues/5488 --- src/hike/screens/main.py | 22 ++++++++++- src/hike/widgets/navigation/history_view.py | 1 - src/hike/widgets/navigation/widget.py | 42 ++++++++++++++++----- src/hike/widgets/viewer.py | 8 ++++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/hike/screens/main.py b/src/hike/screens/main.py index 4c4bd58..0b9fb43 100644 --- a/src/hike/screens/main.py +++ b/src/hike/screens/main.py @@ -5,7 +5,7 @@ from textual import on, work from textual.app import ComposeResult from textual.containers import Horizontal -from textual.widgets import Footer, Header +from textual.widgets import Footer, Header, Markdown ############################################################################## # Textual enhanced imports. @@ -130,6 +130,26 @@ def open_from_history(self, message: OpenFromHistory) -> None: """ self.query_one(Viewer).goto(message.location) + @on(Markdown.TableOfContentsUpdated) + def update_navigation_contents( + self, message: Markdown.TableOfContentsUpdated + ) -> None: + """Handle the table of contents being updated. + + Args: + message: The message broadcasting that the ToC is updated. + """ + self.query_one(Navigation).table_of_contents = message.table_of_contents + + @on(Markdown.TableOfContentsSelected) + def jump_to_content(self, message: Markdown.TableOfContentsSelected) -> None: + """Jump to a specific location in the current document. + + Args: + message: The message request the jump. + """ + self.query_one(Viewer).jump_to_content(message.block_id) + @on(Help) def action_help_command(self) -> None: """Toggle the display of the help panel.""" diff --git a/src/hike/widgets/navigation/history_view.py b/src/hike/widgets/navigation/history_view.py index af8160e..432bf6d 100644 --- a/src/hike/widgets/navigation/history_view.py +++ b/src/hike/widgets/navigation/history_view.py @@ -59,7 +59,6 @@ class HistoryView(EnhancedOptionList): HistoryView { height: 1fr; border: none; - background: transparent; &:focus { border: none; } diff --git a/src/hike/widgets/navigation/widget.py b/src/hike/widgets/navigation/widget.py index 51da16e..40911c7 100644 --- a/src/hike/widgets/navigation/widget.py +++ b/src/hike/widgets/navigation/widget.py @@ -1,13 +1,17 @@ """Provides the navigation panel widget.""" +############################################################################## +# Backward compatibility. +from __future__ import annotations + ############################################################################## # Textual imports. from textual import on from textual.app import ComposeResult from textual.containers import Vertical -from textual.events import DescendantBlur, DescendantFocus from textual.reactive import var -from textual.widgets import Placeholder, TabbedContent +from textual.widgets import Markdown, Placeholder, TabbedContent, Tree +from textual.widgets.markdown import MarkdownTableOfContents, TableOfContentsType ############################################################################## # Local imports. @@ -24,37 +28,55 @@ class Navigation(Vertical): width: 27%; dock: left; background: transparent; + &.--dock-right { dock: right; } + #tabs-list { background: $panel; } + + /* https://github.com/Textualize/textual/issues/5488 */ + MarkdownTableOfContents, &:focus-within MarkdownTableOfContents { + background: transparent; + width: 1fr; + Tree { + background: transparent; + } + } + + /* https://github.com/Textualize/textual/issues/5488 */ + HistoryView, &:focus-within HistoryView { + background: transparent; + } } """ dock_right: var[bool] = var(False) """Should the navigation dock to the right?""" + table_of_contents: var[TableOfContentsType | None] = var(None) + _history: var[HistoryView | None] = var(None) """The history display.""" - @on(DescendantBlur) - @on(DescendantFocus) - def _textual_5488_workaround(self) -> None: - """Workaround for https://github.com/Textualize/textual/issues/5488""" - for widget in self.query(HistoryView): - widget._refresh_lines() - def _watch_dock_right(self) -> None: """React to the dock toggle being changed.""" self.set_class(self.dock_right, "--dock-right") + def _watch_table_of_contents(self) -> None: + """React to the table of content being updated.""" + self.query_one( + MarkdownTableOfContents + ).table_of_contents = self.table_of_contents + self.query_one("MarkdownTableOfContents Tree", Tree).cursor_line = 0 + def compose(self) -> ComposeResult: """Compose the content of the widget.""" self._history = HistoryView() with TabbedContent("Content", "Local", "Bookmarks", "History"): - yield Placeholder() + yield MarkdownTableOfContents(Markdown()) yield Placeholder() yield Placeholder() yield self._history diff --git a/src/hike/widgets/viewer.py b/src/hike/widgets/viewer.py index a05bd6a..659b39c 100644 --- a/src/hike/widgets/viewer.py +++ b/src/hike/widgets/viewer.py @@ -248,5 +248,13 @@ def forward(self) -> None: if self.history.forward(): self._visit_from_history() + def jump_to_content(self, block_id: str) -> None: + """Jump to some content in the current document. + + Args: + block_id: The ID of the content to jump to. + """ + self.scroll_to_widget(self.query_one(f"#{block_id}"), top=True) + ### viewer.py ends here