view and open
This commit is contained in:
@@ -61,7 +61,8 @@ class OneDriveTUI(App):
|
|||||||
self.access_token = None
|
self.access_token = None
|
||||||
self.drives = []
|
self.drives = []
|
||||||
self.followed_items = []
|
self.followed_items = []
|
||||||
self.current_items = [] # Store currently displayed items
|
|
||||||
|
self.current_items = {} # Store currently displayed items
|
||||||
self.msal_app = None
|
self.msal_app = None
|
||||||
self.cache = msal.SerializableTokenCache()
|
self.cache = msal.SerializableTokenCache()
|
||||||
# Read Azure app credentials from environment variables
|
# Read Azure app credentials from environment variables
|
||||||
@@ -105,6 +106,7 @@ class OneDriveTUI(App):
|
|||||||
self.query_one("#view_options").border_title = "My Files"
|
self.query_one("#view_options").border_title = "My Files"
|
||||||
# Initialize the table
|
# Initialize the table
|
||||||
table = self.query_one("#items_table")
|
table = self.query_one("#items_table")
|
||||||
|
table.cursor_type = "row"
|
||||||
table.add_columns("Type", "Name", "Last Modified", "Size", "Web URL")
|
table.add_columns("Type", "Name", "Last Modified", "Size", "Web URL")
|
||||||
|
|
||||||
# Load cached token if available
|
# Load cached token if available
|
||||||
@@ -256,29 +258,60 @@ class OneDriveTUI(App):
|
|||||||
if self.selected_drive_id:
|
if self.selected_drive_id:
|
||||||
self.load_root_items()
|
self.load_root_items()
|
||||||
|
|
||||||
|
async def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
|
||||||
|
"""Handle row selection in the items table."""
|
||||||
|
selected_id = event.row_key.value
|
||||||
|
self.open_item(selected_id)
|
||||||
|
|
||||||
|
async def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
|
||||||
|
self.selected_item_id = event.row_key.value
|
||||||
|
|
||||||
|
def open_item(self, selected_id: str):
|
||||||
|
if selected_id:
|
||||||
|
# Get an item from current items by ID string
|
||||||
|
selected_row = self.current_items[selected_id]
|
||||||
|
|
||||||
|
item_name = selected_row.get("name")
|
||||||
|
|
||||||
|
# Check if it's a folder
|
||||||
|
is_folder = bool(selected_row.get("folder"))
|
||||||
|
|
||||||
|
if is_folder:
|
||||||
|
self.notify(f"Selected folder: {item_name}")
|
||||||
|
# Load items in the folder
|
||||||
|
self.query_one("#status_label").update(f"Loading items in folder: {item_name}")
|
||||||
|
self.load_root_items(folder_id=selected_id, drive_id=selected_row.get("parentReference", {}).get("driveId", self.selected_drive_id))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.notify(f"Selected file: {item_name}")
|
||||||
|
self.action_view_document()
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def load_root_items(self):
|
async def load_root_items(self, folder_id: str = "", drive_id: str = ""):
|
||||||
"""Load root items from the selected drive."""
|
"""Load root items from the selected drive."""
|
||||||
if not self.access_token or not self.selected_drive_id:
|
if not self.access_token or not self.selected_drive_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.query_one("#status_label").update("Loading root items...")
|
self.query_one("#status_label").update("Loading root items...")
|
||||||
headers = {"Authorization": f"Bearer {self.access_token}"}
|
headers = {"Authorization": f"Bearer {self.access_token}"}
|
||||||
|
url = f"https://graph.microsoft.com/v1.0/me/drive/root/children"
|
||||||
|
|
||||||
|
if folder_id and drive_id:
|
||||||
|
url = f"https://graph.microsoft.com/v1.0/drives/{drive_id}/items/{folder_id}/children"
|
||||||
|
self.selected_drive_id = drive_id
|
||||||
try:
|
try:
|
||||||
url = f"https://graph.microsoft.com/v1.0/me/drive/root/children"
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(url, headers=headers) as response:
|
async with session.get(url, headers=headers) as response:
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
self.notify(f"Failed to load root items: {response.status}", severity="error")
|
self.notify(f"Failed to load drive items: {response.status}", severity="error")
|
||||||
return
|
return
|
||||||
|
|
||||||
items_data = await response.json()
|
items_data = await response.json()
|
||||||
self.current_items = items_data.get("value", [])
|
|
||||||
|
|
||||||
# Update the table with the root items
|
# Update the table with the root items
|
||||||
self.update_items_table(self.current_items, is_root_view=True)
|
self.update_items_table(items_data.get("value", []), is_root_view=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.notify(f"Error loading root items: {str(e)}", severity="error")
|
self.notify(f"Error loading root items: {str(e)}", severity="error")
|
||||||
|
|
||||||
@@ -303,11 +336,11 @@ class OneDriveTUI(App):
|
|||||||
return
|
return
|
||||||
|
|
||||||
items_data = await response.json()
|
items_data = await response.json()
|
||||||
self.followed_items = items_data.get("value", [])
|
followed_items = items_data.get("value", [])
|
||||||
self.current_items = self.followed_items
|
|
||||||
|
|
||||||
# Update the table with the followed items
|
# Update the table with the followed items
|
||||||
self.update_items_table(self.followed_items)
|
self.update_items_table(followed_items)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.notify(f"Error loading followed items: {str(e)}", severity="error")
|
self.notify(f"Error loading followed items: {str(e)}", severity="error")
|
||||||
|
|
||||||
@@ -315,7 +348,7 @@ class OneDriveTUI(App):
|
|||||||
|
|
||||||
def update_items_table(self, items, is_root_view=False):
|
def update_items_table(self, items, is_root_view=False):
|
||||||
"""Update the table with the given items."""
|
"""Update the table with the given items."""
|
||||||
table = self.query_one("#items_table")
|
table = self.query_one(DataTable)
|
||||||
table.clear()
|
table.clear()
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
@@ -356,8 +389,11 @@ class OneDriveTUI(App):
|
|||||||
size_str = "N/A"
|
size_str = "N/A"
|
||||||
|
|
||||||
web_url = item.get("webUrl", "")
|
web_url = item.get("webUrl", "")
|
||||||
|
item_id = item.get("id")
|
||||||
table.add_row(item_type, name, last_modified, size_str, web_url)
|
item_drive_id = item.get("parentReference", {}).get("driveId", self.selected_drive_id)
|
||||||
|
row_key = table.add_row(item_type, name, last_modified, size_str, web_url, key=item.get("id"))
|
||||||
|
# add item to to the list of current items keyed by row_key so we can look up all information later
|
||||||
|
self.current_items[row_key] = item
|
||||||
|
|
||||||
async def action_next_view(self) -> None:
|
async def action_next_view(self) -> None:
|
||||||
"""Switch to the next view (Following/Root)."""
|
"""Switch to the next view (Following/Root)."""
|
||||||
@@ -400,44 +436,20 @@ class OneDriveTUI(App):
|
|||||||
# Use Textual's built-in open_url method
|
# Use Textual's built-in open_url method
|
||||||
self.app.open_url(web_url)
|
self.app.open_url(web_url)
|
||||||
|
|
||||||
async def action_view_document(self) -> None:
|
def action_view_document(self) -> None:
|
||||||
"""View the selected document using the DocumentViewerScreen."""
|
"""View the selected document using the DocumentViewerScreen."""
|
||||||
table = self.query_one("#items_table")
|
|
||||||
if table.cursor_row is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the name of the selected item
|
# Get the name of the selected item
|
||||||
selected_row = table.get_row_at(table.cursor_row)
|
|
||||||
|
selected_row = self.current_items.get(self.selected_item_id)
|
||||||
if not selected_row:
|
if not selected_row:
|
||||||
return
|
return
|
||||||
|
|
||||||
selected_name = selected_row[1]
|
selected_name = selected_row.get("name")
|
||||||
|
drive_id = selected_row.get("parentReference", {}).get("driveId", self.selected_drive_id)
|
||||||
# Find the item in our list to get its ID
|
web_url = selected_row.get("webUrl", "")
|
||||||
selected_item = None
|
# Open the document viewer screen with all required details
|
||||||
for item in self.current_items:
|
viewer = DocumentViewerScreen(self.selected_item_id, selected_name, self.access_token, drive_id)
|
||||||
if item.get("name") == selected_name:
|
viewer.web_url = web_url # Pass the webUrl to the viewer
|
||||||
selected_item = item
|
|
||||||
break
|
|
||||||
|
|
||||||
if not selected_item:
|
|
||||||
self.notify("Could not find the selected item details", severity="error")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check if it's a folder - cannot view folders
|
|
||||||
if selected_item.get("folder"):
|
|
||||||
self.notify("Cannot preview folders. Use 'Open URL' to view in browser.", severity="warning")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get the item ID
|
|
||||||
item_id = selected_item.get("id")
|
|
||||||
|
|
||||||
if not item_id:
|
|
||||||
self.notify("Item ID not found", severity="error")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Open the document viewer screen
|
|
||||||
viewer = DocumentViewerScreen(item_id, selected_name, self.access_token, selected_item.get("parentReference").get("driveId"))
|
|
||||||
self.push_screen(viewer)
|
self.push_screen(viewer)
|
||||||
|
|
||||||
async def action_quit(self) -> None:
|
async def action_quit(self) -> None:
|
||||||
|
|||||||
@@ -37,20 +37,18 @@
|
|||||||
#content_container {
|
#content_container {
|
||||||
margin-top: 1;
|
margin-top: 1;
|
||||||
height: 1fr;
|
height: 1fr;
|
||||||
border: round $accent;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status and loading elements */
|
/* Status and loading elements */
|
||||||
#status_label {
|
#status_label {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 1;
|
color: $accent;
|
||||||
|
padding:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loading {
|
|
||||||
align: center middle;
|
|
||||||
margin: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#view_options {
|
#view_options {
|
||||||
border: round $secondary;
|
border: round $secondary;
|
||||||
@@ -124,13 +122,13 @@
|
|||||||
|
|
||||||
#document_title {
|
#document_title {
|
||||||
color: $accent;
|
color: $accent;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
padding: 0 1;
|
|
||||||
text-style: bold;
|
text-style: bold;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
width: 1fr;
|
width: 1fr;
|
||||||
height: 3;
|
height: 1;
|
||||||
align: left middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#plaintext_content {
|
#plaintext_content {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import os
|
|||||||
import io
|
import io
|
||||||
import asyncio
|
import asyncio
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Optional
|
from typing import Optional, Tuple, Set
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import mammoth
|
import mammoth
|
||||||
@@ -10,20 +11,50 @@ from docx import Document
|
|||||||
|
|
||||||
from textual.app import ComposeResult
|
from textual.app import ComposeResult
|
||||||
from textual.binding import Binding
|
from textual.binding import Binding
|
||||||
from textual.containers import Container, ScrollableContainer, Horizontal
|
from textual.containers import Container, ScrollableContainer, Horizontal, Vertical
|
||||||
from textual.screen import Screen
|
from textual.screen import Screen
|
||||||
from textual.widgets import Label, Markdown, LoadingIndicator, Button, Footer
|
from textual.widgets import Label, Markdown, LoadingIndicator, Button, Footer
|
||||||
from textual.worker import Worker, get_current_worker
|
from textual.worker import Worker, get_current_worker
|
||||||
from textual import work
|
from textual import work
|
||||||
|
from textual.reactive import Reactive, reactive
|
||||||
|
|
||||||
|
|
||||||
|
# Define convertible formats
|
||||||
|
PDF_CONVERTIBLE_FORMATS = {
|
||||||
|
"doc", "docx", "epub", "eml", "htm", "html", "md", "msg", "odp",
|
||||||
|
"ods", "odt", "pps", "ppsx", "ppt", "pptx", "rtf", "tif", "tiff",
|
||||||
|
"xls", "xlsm", "xlsx"
|
||||||
|
}
|
||||||
|
|
||||||
|
JPG_CONVERTIBLE_FORMATS = {
|
||||||
|
"3g2", "3gp", "3gp2", "3gpp", "3mf", "ai", "arw", "asf", "avi",
|
||||||
|
"bas", "bash", "bat", "bmp", "c", "cbl", "cmd", "cool", "cpp",
|
||||||
|
"cr2", "crw", "cs", "css", "csv", "cur", "dcm", "dcm30", "dic",
|
||||||
|
"dicm", "dicom", "dng", "doc", "docx", "dwg", "eml", "epi", "eps",
|
||||||
|
"epsf", "epsi", "epub", "erf", "fbx", "fppx", "gif", "glb", "h",
|
||||||
|
"hcp", "heic", "heif", "htm", "html", "ico", "icon", "java", "jfif",
|
||||||
|
"jpeg", "jpg", "js", "json", "key", "log", "m2ts", "m4a", "m4v",
|
||||||
|
"markdown", "md", "mef", "mov", "movie", "mp3", "mp4", "mp4v", "mrw",
|
||||||
|
"msg", "mts", "nef", "nrw", "numbers", "obj", "odp", "odt", "ogg",
|
||||||
|
"orf", "pages", "pano", "pdf", "pef", "php", "pict", "pl", "ply",
|
||||||
|
"png", "pot", "potm", "potx", "pps", "ppsx", "ppsxm", "ppt", "pptm",
|
||||||
|
"pptx", "ps", "ps1", "psb", "psd", "py", "raw", "rb", "rtf", "rw1",
|
||||||
|
"rw2", "sh", "sketch", "sql", "sr2", "stl", "tif", "tiff", "ts",
|
||||||
|
"txt", "vb", "webm", "wma", "wmv", "xaml", "xbm", "xcf", "xd", "xml",
|
||||||
|
"xpm", "yaml", "yml"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DocumentViewerScreen(Screen):
|
class DocumentViewerScreen(Screen):
|
||||||
"""Screen for viewing document content from OneDrive items."""
|
"""Screen for viewing document content from OneDrive items."""
|
||||||
|
web_url: Reactive[str] = reactive("")
|
||||||
|
download_url: Reactive[str] = reactive("")
|
||||||
|
|
||||||
BINDINGS = [
|
BINDINGS = [
|
||||||
Binding("escape", "close", "Close"),
|
Binding("escape", "close", "Close"),
|
||||||
Binding("q", "close", "Close"),
|
Binding("q", "close", "Close"),
|
||||||
Binding("m", "toggle_mode", "Toggle Mode"),
|
Binding("m", "toggle_mode", "Toggle Mode"),
|
||||||
|
Binding("e", "export_and_open", "Export & Open"),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, item_id: str, item_name: str, access_token: str, drive_id: str):
|
def __init__(self, item_id: str, item_name: str, access_token: str, drive_id: str):
|
||||||
@@ -33,6 +64,7 @@ class DocumentViewerScreen(Screen):
|
|||||||
item_id: The ID of the item to view.
|
item_id: The ID of the item to view.
|
||||||
item_name: The name of the item to display.
|
item_name: The name of the item to display.
|
||||||
access_token: The access token for API requests.
|
access_token: The access token for API requests.
|
||||||
|
drive_id: The ID of the drive containing the item.
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.item_id = item_id
|
self.item_id = item_id
|
||||||
@@ -44,15 +76,18 @@ class DocumentViewerScreen(Screen):
|
|||||||
self.is_markdown_mode = False
|
self.is_markdown_mode = False
|
||||||
self.content_type = None
|
self.content_type = None
|
||||||
self.raw_content = None
|
self.raw_content = None
|
||||||
|
self.file_extension = Path(item_name).suffix.lower().lstrip('.')
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
"""Compose the document viewer screen."""
|
"""Compose the document viewer screen."""
|
||||||
yield Container(
|
yield Container(
|
||||||
Horizontal(
|
Horizontal(
|
||||||
Label(f"Viewing: {self.item_name}", id="document_title"),
|
Container(
|
||||||
|
Label(f"Viewing: {self.item_name}", id="document_title"),
|
||||||
|
Label(f'[link="{self.web_url}"]Open on Web[/link] | [link="{self.download_url}"]Download File[/link]', id="document_link"),
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
Button("Close", id="close_button"),
|
Button("Close", id="close_button"),
|
||||||
Button("Toggle Mode", id="toggle_mode_button"),
|
|
||||||
id="button_container"
|
id="button_container"
|
||||||
),
|
),
|
||||||
id="top_container"
|
id="top_container"
|
||||||
@@ -77,17 +112,54 @@ class DocumentViewerScreen(Screen):
|
|||||||
self.dismiss()
|
self.dismiss()
|
||||||
elif event.button.id == "toggle_mode_button":
|
elif event.button.id == "toggle_mode_button":
|
||||||
self.action_toggle_mode()
|
self.action_toggle_mode()
|
||||||
|
elif event.button.id == "export_button":
|
||||||
|
self.action_export_and_open()
|
||||||
|
|
||||||
|
|
||||||
|
def is_convertible_format(self) -> bool:
|
||||||
|
"""Check if the current file is convertible to PDF or JPG."""
|
||||||
|
return (self.file_extension in PDF_CONVERTIBLE_FORMATS or
|
||||||
|
self.file_extension in JPG_CONVERTIBLE_FORMATS)
|
||||||
|
|
||||||
|
def get_conversion_format(self) -> str:
|
||||||
|
"""Get the appropriate conversion format (pdf or jpg) for the current file."""
|
||||||
|
if self.file_extension in PDF_CONVERTIBLE_FORMATS:
|
||||||
|
return "pdf"
|
||||||
|
elif self.file_extension in JPG_CONVERTIBLE_FORMATS:
|
||||||
|
return "jpg"
|
||||||
|
return None
|
||||||
|
|
||||||
@work
|
@work
|
||||||
async def download_document(self) -> None:
|
async def download_document(self) -> None:
|
||||||
"""Download the document content."""
|
"""Download the document content."""
|
||||||
|
|
||||||
|
headers = {"Authorization": f"Bearer {self.access_token}"}
|
||||||
|
try:
|
||||||
|
metadataUrl = f"https://graph.microsoft.com/v1.0/drives/{self.drive_id}/items/{self.item_id}"
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(metadataUrl, headers=headers) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
error_text = await response.text()
|
||||||
|
self.notify(f"Failed to fetch document metadata: {error_text}", severity="error")
|
||||||
|
return
|
||||||
|
|
||||||
|
metadata = await response.json()
|
||||||
|
self.item_name = metadata.get("name", self.item_name)
|
||||||
|
self.file_extension = Path(self.item_name).suffix.lower().lstrip('.')
|
||||||
|
self.download_url = metadata.get("@microsoft.graph.downloadUrl", "")
|
||||||
|
self.web_url = metadata.get("webUrl", "")
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.notify(f"Error downloading document: {str(e)}", severity="error")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = f"https://graph.microsoft.com/v1.0/drives/{self.drive_id}/items/{self.item_id}/content"
|
url = f"https://graph.microsoft.com/v1.0/drives/{self.drive_id}/items/{self.item_id}/content"
|
||||||
headers = {"Authorization": f"Bearer {self.access_token}"}
|
|
||||||
|
|
||||||
# Show loading indicator
|
# Show loading indicator
|
||||||
self.query_one("#content_container").loading = True
|
self.query_one("#content_container").loading = True
|
||||||
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(url, headers=headers) as response:
|
async with session.get(url, headers=headers) as response:
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
@@ -129,7 +201,12 @@ class DocumentViewerScreen(Screen):
|
|||||||
self.update_content_display()
|
self.update_content_display()
|
||||||
else:
|
else:
|
||||||
# For other types, display a generic message
|
# For other types, display a generic message
|
||||||
self.document_content = f"*File: {self.item_name}*\n\nContent type: {self.content_type}\n\nThis file type cannot be displayed in the viewer. Use the 'Open URL' command to view this file in your browser."
|
conversion_info = ""
|
||||||
|
if self.is_convertible_format():
|
||||||
|
conversion_format = self.get_conversion_format()
|
||||||
|
conversion_info = f"\n\nThis file can be converted to {conversion_format.upper()}. Press 'e' or click 'Export & Open' to convert and view."
|
||||||
|
self.document_content = f"*File: {self.item_name}*\n\nContent type: {self.content_type}{conversion_info}\n\nThis file type cannot be displayed directly in the viewer. You could [open in your browser]({self.web_url}), or [download the file]({self.download_url})."
|
||||||
|
self.is_markdown_mode = True
|
||||||
self.update_content_display()
|
self.update_content_display()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.notify(f"Error processing content: {str(e)}", severity="error")
|
self.notify(f"Error processing content: {str(e)}", severity="error")
|
||||||
@@ -175,13 +252,63 @@ class DocumentViewerScreen(Screen):
|
|||||||
plaintext_widget.remove_class("hidden")
|
plaintext_widget.remove_class("hidden")
|
||||||
markdown_widget.add_class("hidden")
|
markdown_widget.add_class("hidden")
|
||||||
|
|
||||||
|
@work
|
||||||
|
async def export_and_open_converted_file(self) -> None:
|
||||||
|
"""Export the file in converted format and open it."""
|
||||||
|
if not self.is_convertible_format():
|
||||||
|
self.notify("This file format cannot be converted.", severity="warning")
|
||||||
|
return
|
||||||
|
|
||||||
|
conversion_format = self.get_conversion_format()
|
||||||
|
if not conversion_format:
|
||||||
|
self.notify("No appropriate conversion format found.", severity="error")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Build the URL with the format parameter
|
||||||
|
url = f"https://graph.microsoft.com/v1.0/drives/{self.drive_id}/items/{self.item_id}/content?format={conversion_format}"
|
||||||
|
headers = {"Authorization": f"Bearer {self.access_token}"}
|
||||||
|
|
||||||
|
# Download the converted file
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(url, headers=headers) as response:
|
||||||
|
if response.status != 200:
|
||||||
|
error_text = await response.text()
|
||||||
|
self.notify(f"Failed to export document: {error_text}", severity="error")
|
||||||
|
return
|
||||||
|
|
||||||
|
converted_content = await response.read()
|
||||||
|
|
||||||
|
# Create temporary file with the right extension
|
||||||
|
file_name = f"{os.path.splitext(self.item_name)[0]}.{conversion_format}"
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=f".{conversion_format}",
|
||||||
|
delete=False,
|
||||||
|
prefix=f"onedrive_export_") as temp_file:
|
||||||
|
temp_file.write(converted_content)
|
||||||
|
temp_path = temp_file.name
|
||||||
|
|
||||||
|
# Open the file using the system default application
|
||||||
|
self.notify(f"Opening exported {conversion_format.upper()} file: {file_name}")
|
||||||
|
self.app.open_url(f"file://{temp_path}")
|
||||||
|
self.query_one("#content_container").loading = False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.notify(f"Error exporting document: {str(e)}", severity="error")
|
||||||
|
|
||||||
async def action_toggle_mode(self) -> None:
|
async def action_toggle_mode(self) -> None:
|
||||||
"""Toggle between Markdown and plaintext display modes."""
|
"""Toggle between Markdown and plaintext display modes."""
|
||||||
|
self.notify("Switching Modes", severity="info")
|
||||||
self.is_markdown_mode = not self.is_markdown_mode
|
self.is_markdown_mode = not self.is_markdown_mode
|
||||||
self.update_content_display()
|
self.update_content_display()
|
||||||
mode_name = "Markdown" if self.is_markdown_mode else "Plain Text"
|
mode_name = "Markdown" if self.is_markdown_mode else "Plain Text"
|
||||||
self.notify(f"Switched to {mode_name} mode")
|
self.notify(f"Switched to {mode_name} mode")
|
||||||
|
|
||||||
|
async def action_export_and_open(self) -> None:
|
||||||
|
"""Export the file in converted format and open it."""
|
||||||
|
self.query_one("#content_container").loading = True
|
||||||
|
self.notify("Exporting and opening the converted file...")
|
||||||
|
self.export_and_open_converted_file()
|
||||||
|
|
||||||
async def action_close(self) -> None:
|
async def action_close(self) -> None:
|
||||||
"""Close the document viewer screen."""
|
"""Close the document viewer screen."""
|
||||||
self.dismiss()
|
self.dismiss()
|
||||||
|
|||||||
Reference in New Issue
Block a user