diff --git a/repo_autoindex/_impl/base.py b/repo_autoindex/_impl/base.py
index 55a8121..26b67c6 100644
--- a/repo_autoindex/_impl/base.py
+++ b/repo_autoindex/_impl/base.py
@@ -56,6 +56,23 @@ class IndexEntry:
padding: str = ""
icon: str = ICON_OTHER
+ @property
+ def sort_key(self):
+ # Returns a suggested sort key for displaying entries in
+ # a UI.
+
+ priority = 0
+
+ # Folders should come first
+ if self.href.endswith("/"):
+ priority -= 1
+ # And special folders like ".." even earlier
+ if self.href.startswith("."):
+ priority -= 1
+
+ # Entries sort by the priority we calculated, and then by name
+ return (priority, self.href)
+
class Repo(ABC):
def __init__(
diff --git a/repo_autoindex/_impl/tree.py b/repo_autoindex/_impl/tree.py
index 168bda4..3fa466b 100644
--- a/repo_autoindex/_impl/tree.py
+++ b/repo_autoindex/_impl/tree.py
@@ -70,4 +70,6 @@ def treeify(
]
)
+ out.entries.sort(key=lambda entry: entry.sort_key)
+
return out
diff --git a/tests/test_kickstart_render_typical.py b/tests/test_kickstart_render_typical.py
index 6dcac1c..851fdf3 100644
--- a/tests/test_kickstart_render_typical.py
+++ b/tests/test_kickstart_render_typical.py
@@ -1,4 +1,5 @@
import io
+import re
from typing import BinaryIO, Optional
import textwrap
@@ -619,6 +620,30 @@ async def test_typical_index():
assert '' in by_relative_dir["images/pxeboot"].content
+ # Sample the order of entries in some of the listings.
+ # Directories are expected to come first.
+ links = re.findall(r'