feat: Enhance WebDAV file management and UI

- Add functionality to create new collections via API
- Implement copy and move operations between collections
- Improve image rendering in markdown preview with relative path resolution
- Add support for previewing binary files (images, PDFs)
- Refactor modal styling to use flat buttons and improve accessibility
This commit is contained in:
Mahmoud-Emad
2025-10-26 17:29:45 +03:00
parent 0ed6bcf1f2
commit f319f29d4c
20 changed files with 1679 additions and 113 deletions

View File

@@ -28,8 +28,16 @@ class MarkdownEditorApp:
def load_config(self, config_path):
"""Load configuration from YAML file"""
self.config_path = config_path
with open(config_path, 'r') as f:
return yaml.safe_load(f)
def save_config(self):
"""Save configuration to YAML file"""
# Update config with current collections
self.config['collections'] = self.collections
with open(self.config_path, 'w') as f:
yaml.dump(self.config, f, default_flow_style=False, sort_keys=False)
def setup_collections(self):
"""Create collection directories if they don't exist"""
@@ -92,6 +100,10 @@ class MarkdownEditorApp:
if path == '/fs/' and method == 'GET':
return self.handle_collections_list(environ, start_response)
# API to create new collection
if path == '/fs/' and method == 'POST':
return self.handle_create_collection(environ, start_response)
# Check if path starts with a collection name (for SPA routing)
# This handles URLs like /notes/ttt or /documents/file.md
# MUST be checked BEFORE WebDAV routing to prevent WebDAV from intercepting SPA routes
@@ -113,14 +125,86 @@ class MarkdownEditorApp:
"""Return list of available collections"""
collections = list(self.collections.keys())
response_body = json.dumps(collections).encode('utf-8')
start_response('200 OK', [
('Content-Type', 'application/json'),
('Content-Length', str(len(response_body))),
('Access-Control-Allow-Origin', '*')
])
return [response_body]
def handle_create_collection(self, environ, start_response):
"""Create a new collection"""
try:
# Read request body
content_length = int(environ.get('CONTENT_LENGTH', 0))
request_body = environ['wsgi.input'].read(content_length)
data = json.loads(request_body.decode('utf-8'))
collection_name = data.get('name')
if not collection_name:
start_response('400 Bad Request', [('Content-Type', 'application/json')])
return [json.dumps({'error': 'Collection name is required'}).encode('utf-8')]
# Check if collection already exists
if collection_name in self.collections:
start_response('409 Conflict', [('Content-Type', 'application/json')])
return [json.dumps({'error': f'Collection "{collection_name}" already exists'}).encode('utf-8')]
# Create collection directory
collection_path = Path(f'./collections/{collection_name}')
collection_path.mkdir(parents=True, exist_ok=True)
# Create images subdirectory
images_path = collection_path / 'images'
images_path.mkdir(exist_ok=True)
# Add to collections dict
self.collections[collection_name] = {
'path': str(collection_path),
'description': f'User-created collection: {collection_name}'
}
# Update config file
self.save_config()
# Add to WebDAV provider mapping
from wsgidav.fs_dav_provider import FilesystemProvider
provider_path = os.path.abspath(str(collection_path))
provider_key = f'/fs/{collection_name}'
# Use the add_provider method if available, otherwise add directly to provider_map
provider = FilesystemProvider(provider_path)
if hasattr(self.webdav_app, 'add_provider'):
self.webdav_app.add_provider(provider_key, provider)
print(f"Added provider using add_provider(): {provider_key}")
else:
self.webdav_app.provider_map[provider_key] = provider
print(f"Added provider to provider_map: {provider_key}")
# Also update sorted_share_list if it exists
if hasattr(self.webdav_app, 'sorted_share_list'):
if provider_key not in self.webdav_app.sorted_share_list:
self.webdav_app.sorted_share_list.append(provider_key)
self.webdav_app.sorted_share_list.sort(reverse=True)
print(f"Updated sorted_share_list")
print(f"Created collection '{collection_name}' at {provider_path}")
response_body = json.dumps({'success': True, 'name': collection_name}).encode('utf-8')
start_response('201 Created', [
('Content-Type', 'application/json'),
('Content-Length', str(len(response_body))),
('Access-Control-Allow-Origin', '*')
])
return [response_body]
except Exception as e:
print(f"Error creating collection: {e}")
start_response('500 Internal Server Error', [('Content-Type', 'application/json')])
return [json.dumps({'error': str(e)}).encode('utf-8')]
def handle_static(self, environ, start_response):
"""Serve static files"""