h
This commit is contained in:
@@ -1838,7 +1838,13 @@ def extract_item_metadata(item: Any) -> Dict[str, Any]:
|
|||||||
|
|
||||||
# Use existing extractors from match-standard result table columns
|
# Use existing extractors from match-standard result table columns
|
||||||
title = extract_title_value(item)
|
title = extract_title_value(item)
|
||||||
if title: out["Title"] = title
|
if title:
|
||||||
|
out["Title"] = title
|
||||||
|
else:
|
||||||
|
# Fallback for raw dicts
|
||||||
|
data = _as_dict(item) or {}
|
||||||
|
t = data.get("title") or data.get("name") or data.get("TITLE")
|
||||||
|
if t: out["Title"] = t
|
||||||
|
|
||||||
hv = extract_hash_value(item)
|
hv = extract_hash_value(item)
|
||||||
if hv: out["Hash"] = hv
|
if hv: out["Hash"] = hv
|
||||||
@@ -1852,10 +1858,18 @@ def extract_item_metadata(item: Any) -> Dict[str, Any]:
|
|||||||
if path: out["Path"] = path
|
if path: out["Path"] = path
|
||||||
|
|
||||||
ext = extract_ext_value(item)
|
ext = extract_ext_value(item)
|
||||||
if ext: out["Ext"] = ext
|
if ext:
|
||||||
|
out["Ext"] = ext
|
||||||
|
else:
|
||||||
|
e = data.get("ext") or data.get("extension")
|
||||||
|
if e: out["Ext"] = e
|
||||||
|
|
||||||
size = extract_size_bytes_value(item)
|
size = extract_size_bytes_value(item)
|
||||||
if size: out["Size"] = size
|
if size:
|
||||||
|
out["Size"] = size
|
||||||
|
else:
|
||||||
|
s = data.get("size") or data.get("size_bytes")
|
||||||
|
if s: out["Size"] = s
|
||||||
|
|
||||||
# Duration
|
# Duration
|
||||||
dur = _get_first_dict_value(data, ["duration_seconds", "duration"])
|
dur = _get_first_dict_value(data, ["duration_seconds", "duration"])
|
||||||
@@ -1864,11 +1878,17 @@ def extract_item_metadata(item: Any) -> Dict[str, Any]:
|
|||||||
|
|
||||||
# URL
|
# URL
|
||||||
url = _get_first_dict_value(data, ["url", "URL"])
|
url = _get_first_dict_value(data, ["url", "URL"])
|
||||||
if url: out["Url"] = url
|
if url:
|
||||||
|
out["Url"] = url
|
||||||
|
else:
|
||||||
|
out["Url"] = None # Explicitly None for <null> display
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
rels = _get_first_dict_value(data, ["relationships", "rel"])
|
rels = _get_first_dict_value(data, ["relationships", "rel"])
|
||||||
if rels: out["Relations"] = rels
|
if rels:
|
||||||
|
out["Relations"] = rels
|
||||||
|
else:
|
||||||
|
out["Relations"] = None
|
||||||
|
|
||||||
# Tags Summary
|
# Tags Summary
|
||||||
tags = _get_first_dict_value(data, ["tags", "tag"])
|
tags = _get_first_dict_value(data, ["tags", "tag"])
|
||||||
@@ -1901,10 +1921,10 @@ class ItemDetailView(ResultTable):
|
|||||||
from rich.columns import Columns
|
from rich.columns import Columns
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
# 1. Create Detail Grid
|
# 1. Create Detail Grid (matching rich_display.py style)
|
||||||
details_table = RichTable(show_header=False, box=None, padding=(0, 2), expand=True)
|
details_table = RichTable.grid(expand=True, padding=(0, 2))
|
||||||
details_table.add_column("Key", style="bold cyan", justify="right", width=12)
|
details_table.add_column("Key", style="cyan", justify="right", width=15)
|
||||||
details_table.add_column("Value")
|
details_table.add_column("Value", style="white")
|
||||||
|
|
||||||
# Canonical display order for metadata
|
# Canonical display order for metadata
|
||||||
order = ["Title", "Hash", "Store", "Path", "Ext", "Size", "Duration", "Url", "Relations"]
|
order = ["Title", "Hash", "Store", "Path", "Ext", "Size", "Duration", "Url", "Relations"]
|
||||||
@@ -1912,9 +1932,16 @@ class ItemDetailView(ResultTable):
|
|||||||
has_details = False
|
has_details = False
|
||||||
# Add ordered items first
|
# Add ordered items first
|
||||||
for key in order:
|
for key in order:
|
||||||
val = self.item_metadata.get(key) or self.item_metadata.get(key.lower()) or self.item_metadata.get(key.upper())
|
val = self.item_metadata.get(key)
|
||||||
|
if val is None:
|
||||||
|
val = self.item_metadata.get(key.lower())
|
||||||
|
if val is None:
|
||||||
|
val = self.item_metadata.get(key.upper())
|
||||||
|
|
||||||
# Special formatting for certain types
|
# Special formatting for certain types
|
||||||
|
if key == "Title" and val:
|
||||||
|
val = f"[bold]{val}[/bold]"
|
||||||
|
|
||||||
if key == "Size" and val and isinstance(val, (int, float, str)) and str(val).isdigit():
|
if key == "Size" and val and isinstance(val, (int, float, str)) and str(val).isdigit():
|
||||||
val = _format_size(int(val), integer_only=False)
|
val = _format_size(int(val), integer_only=False)
|
||||||
|
|
||||||
@@ -1924,7 +1951,7 @@ class ItemDetailView(ResultTable):
|
|||||||
else:
|
else:
|
||||||
val = "\n".join([f"[dim]→[/dim] {r}" for r in val])
|
val = "\n".join([f"[dim]→[/dim] {r}" for r in val])
|
||||||
|
|
||||||
if val:
|
if val is not None and val != "":
|
||||||
details_table.add_row(f"{key}:", str(val))
|
details_table.add_row(f"{key}:", str(val))
|
||||||
has_details = True
|
has_details = True
|
||||||
elif key in ["Url", "Relations"]:
|
elif key in ["Url", "Relations"]:
|
||||||
@@ -1936,7 +1963,8 @@ class ItemDetailView(ResultTable):
|
|||||||
for k, v in self.item_metadata.items():
|
for k, v in self.item_metadata.items():
|
||||||
k_norm = k.lower()
|
k_norm = k.lower()
|
||||||
if k_norm not in [x.lower() for x in order] and v and k_norm not in ["tags", "tag"]:
|
if k_norm not in [x.lower() for x in order] and v and k_norm not in ["tags", "tag"]:
|
||||||
details_table.add_row(f"{k.capitalize()}:", str(v))
|
label = k.capitalize() if len(k) > 1 else k.upper()
|
||||||
|
details_table.add_row(f"{label}:", str(v))
|
||||||
has_details = True
|
has_details = True
|
||||||
|
|
||||||
# Tags Summary
|
# Tags Summary
|
||||||
@@ -1950,32 +1978,47 @@ class ItemDetailView(ResultTable):
|
|||||||
details_table.add_row("Tags:", tag_cols)
|
details_table.add_row("Tags:", tag_cols)
|
||||||
has_details = True
|
has_details = True
|
||||||
|
|
||||||
# 2. Get the standard table render
|
# 2. Get the standard table render (if there are rows or a specific title)
|
||||||
original_title = self.title
|
original_title = self.title
|
||||||
original_header_lines = self.header_lines
|
original_header_lines = self.header_lines
|
||||||
self.title = ""
|
self.title = ""
|
||||||
self.header_lines = []
|
self.header_lines = []
|
||||||
|
|
||||||
|
results_renderable = None
|
||||||
|
# We only show the results panel if there's data OR if the user explicitly set a title (cmdlet mode)
|
||||||
|
if self.rows or original_title:
|
||||||
|
self.title = original_title
|
||||||
try:
|
try:
|
||||||
results_renderable = super().to_rich()
|
results_renderable = super().to_rich()
|
||||||
finally:
|
finally:
|
||||||
self.title = original_title
|
self.title = "" # Keep it clean for element assembly
|
||||||
self.header_lines = original_header_lines
|
|
||||||
|
|
||||||
# 3. Assemble components
|
# 3. Assemble components
|
||||||
elements = []
|
elements = []
|
||||||
|
|
||||||
if has_details:
|
if has_details:
|
||||||
elements.append(Panel(details_table, title="Item Details", border_style="blue"))
|
elements.append(Panel(
|
||||||
|
details_table,
|
||||||
|
title="[bold green]Item Details[/bold green]",
|
||||||
|
border_style="green",
|
||||||
|
padding=(1, 2)
|
||||||
|
))
|
||||||
|
|
||||||
# Wrap the results in a titled panel
|
if results_renderable:
|
||||||
|
# If it's a Panel already (from super().to_rich() with title), use it directly
|
||||||
|
# but force the border style to green for consistency
|
||||||
|
if isinstance(results_renderable, Panel):
|
||||||
|
results_renderable.border_style = "green"
|
||||||
|
# Add a bit of padding inside if it contains a table
|
||||||
|
elements.append(results_renderable)
|
||||||
|
else:
|
||||||
|
# Wrap the raw table/text in a titled panel
|
||||||
display_title = "Items"
|
display_title = "Items"
|
||||||
if original_title:
|
if original_title:
|
||||||
display_title = original_title
|
display_title = original_title
|
||||||
|
|
||||||
# Add a bit of padding
|
# Add a bit of padding
|
||||||
results_group = Group(Text(""), results_renderable, Text(""))
|
results_group = Group(Text(""), results_renderable, Text(""))
|
||||||
|
|
||||||
elements.append(Panel(results_group, title=display_title, border_style="green"))
|
elements.append(Panel(results_group, title=display_title, border_style="green"))
|
||||||
|
|
||||||
return Group(*elements)
|
return Group(*elements)
|
||||||
|
|||||||
@@ -262,100 +262,20 @@ def render_image_to_console(image_path: str | Path, max_width: int | None = None
|
|||||||
|
|
||||||
|
|
||||||
def render_item_details_panel(item: Dict[str, Any]) -> None:
|
def render_item_details_panel(item: Dict[str, Any]) -> None:
|
||||||
"""Render a comprehensive details panel for a result item."""
|
"""Render a comprehensive details panel for a result item using unified ItemDetailView."""
|
||||||
from rich.table import Table
|
from SYS.result_table import ItemDetailView, extract_item_metadata
|
||||||
from rich.columns import Columns
|
|
||||||
from rich.panel import Panel
|
|
||||||
|
|
||||||
title = (
|
metadata = extract_item_metadata(item)
|
||||||
item.get("title")
|
|
||||||
or item.get("name")
|
|
||||||
or item.get("TITLE")
|
|
||||||
or "Unnamed Item"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Main layout table for the panel
|
# Create a specialized view with no results rows (only the metadata panel)
|
||||||
details_table = Table.grid(expand=True, padding=(0, 2))
|
# We set no_choice=True to hide the "#" column (not that there are any rows).
|
||||||
details_table.add_column(style="cyan", no_wrap=True, width=15, justify="right")
|
view = ItemDetailView(item_metadata=metadata).set_no_choice(True)
|
||||||
details_table.add_column(style="white")
|
|
||||||
|
|
||||||
# Canonical order
|
# We want to print ONLY the elements from ItemDetailView, so we don't use stdout_console().print(view)
|
||||||
details_table.add_row("Title:", f"[bold]{title}[/bold]")
|
# as that would include the (empty) results panel.
|
||||||
|
# Actually, let's just use to_rich and print it.
|
||||||
if "hash" in item or "hash_hex" in item or "file_hash" in item:
|
|
||||||
h = item.get("hash") or item.get("hash_hex") or item.get("file_hash")
|
|
||||||
details_table.add_row("Hash:", str(h))
|
|
||||||
|
|
||||||
if "store" in item or "table" in item:
|
|
||||||
s = item.get("store") or item.get("table")
|
|
||||||
details_table.add_row("Store:", str(s))
|
|
||||||
|
|
||||||
if "path" in item or "target" in item:
|
|
||||||
path = item.get("path") or item.get("target")
|
|
||||||
# Only show if it doesn't look like a URL (which would go in Url row)
|
|
||||||
if path and not str(path).startswith(("http://", "https://")):
|
|
||||||
details_table.add_row("Path:", str(path))
|
|
||||||
|
|
||||||
if "ext" in item or "extension" in item:
|
|
||||||
ext = item.get("ext") or item.get("extension")
|
|
||||||
details_table.add_row("Ext:", str(ext))
|
|
||||||
|
|
||||||
if "size_bytes" in item or "size" in item:
|
|
||||||
size = item.get("size_bytes") or item.get("size")
|
|
||||||
if isinstance(size, (int, float, str)) and str(size).isdigit():
|
|
||||||
size = int(size)
|
|
||||||
if size > 1024 * 1024 * 1024:
|
|
||||||
size_str = f"{size / (1024*1024*1024):.1f} GB"
|
|
||||||
elif size > 1024 * 1024:
|
|
||||||
size_str = f"{size / (1024*1024):.1f} MB"
|
|
||||||
elif size > 1024:
|
|
||||||
size_str = f"{size / 1024:.1f} KB"
|
|
||||||
else:
|
|
||||||
size_str = f"{size} bytes"
|
|
||||||
details_table.add_row("Size:", size_str)
|
|
||||||
|
|
||||||
# URL(s)
|
|
||||||
urls = item.get("url") or item.get("URL") or []
|
|
||||||
if isinstance(urls, str):
|
|
||||||
urls = [urls]
|
|
||||||
valid_urls = [str(u).strip() for u in urls if str(u).strip()]
|
|
||||||
if valid_urls:
|
|
||||||
url_text = "\n".join(valid_urls)
|
|
||||||
details_table.add_row("Url:", url_text)
|
|
||||||
else:
|
|
||||||
details_table.add_row("Url:", "[dim]<null>[/dim]")
|
|
||||||
|
|
||||||
# Tags
|
|
||||||
tags = item.get("tag") or item.get("tags") or []
|
|
||||||
if isinstance(tags, str):
|
|
||||||
tags = [t.strip() for t in tags.split(",") if t.strip()]
|
|
||||||
if isinstance(tags, list) and tags:
|
|
||||||
tags_sorted = sorted(map(str, tags))
|
|
||||||
tag_cols = Columns([f"[dim]#[/dim]{t}" for t in tags_sorted], equal=True, expand=True)
|
|
||||||
details_table.add_row("", "") # Spacer
|
|
||||||
details_table.add_row("Tags:", tag_cols)
|
|
||||||
|
|
||||||
# Relationships (if any)
|
|
||||||
rels = item.get("relationships") or item.get("rel") or []
|
|
||||||
if isinstance(rels, list) and rels:
|
|
||||||
# Check for list of dicts (from get-relationship) or list of strings
|
|
||||||
if rels and isinstance(rels[0], dict):
|
|
||||||
rel_text = "\n".join([f"[dim]→[/dim] {r.get('type','rel')}: {r.get('title','?')}" for r in rels])
|
|
||||||
else:
|
|
||||||
rel_text = "\n".join([f"[dim]→[/dim] {r}" for r in rels])
|
|
||||||
details_table.add_row("Relations:", rel_text)
|
|
||||||
else:
|
|
||||||
details_table.add_row("Relations:", "[dim]<null>[/dim]")
|
|
||||||
|
|
||||||
panel = Panel(
|
|
||||||
details_table,
|
|
||||||
title=f"[bold green]Item Details[/bold green]",
|
|
||||||
border_style="green",
|
|
||||||
padding=(1, 2),
|
|
||||||
expand=True
|
|
||||||
)
|
|
||||||
|
|
||||||
stdout_console().print()
|
stdout_console().print()
|
||||||
stdout_console().print(panel)
|
stdout_console().print(view.to_rich())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user