feat: add project file system with CRUD, upload, inline editing, and video attachment support
- Add new `/projects/{slug}/files` endpoint group for per-project filesystem operations including directory and file CRUD, upload, and inline editing with public read and owner write access
- Extend attachment system to support video formats (webm, ogv, mov, m4v) with proper file icons and MIME types
- Implement configurable allowed file types via `allowed_file_types` site setting, replacing hardcoded `ALLOWED_UPLOAD_TYPES` with dynamic `allowed_extensions()` and `is_extension_allowed()` functions
- Add `delete_all_project_files()` call in `delete_content_item()` to clean up project files when a project is deleted
- Create database indexes on `project_files` table for `(project_uid, path)` and `(project_uid, parent_path)` to optimize file lookups
- Introduce `docs_prose.py` module with `render_prose()` function that renders Markdown content inside `data-render` divs using mistune, enabling dynamic prose rendering in documentation pages
- Enhance docs search with Markdown-aware text stripping (`_demarkdown()`) and improved HTML/script/style sanitization for better search indexing
- Update documentation API samples to reflect new attachment response fields (`is_image`, `is_video`, `mime_type`) and note video format support
- Update README to document the new project files endpoint and clarify AI gateway attribution for guest Devii sessions
This commit is contained in:
@@ -7,6 +7,7 @@ export class DeviiTerminal {
|
||||
constructor() {
|
||||
this.element = null;
|
||||
this.avatar = null;
|
||||
this.bindEscapeTrigger();
|
||||
this.ready = this.init();
|
||||
}
|
||||
|
||||
@@ -42,6 +43,32 @@ export class DeviiTerminal {
|
||||
}
|
||||
}
|
||||
|
||||
bindEscapeTrigger() {
|
||||
document.addEventListener(
|
||||
"keydown",
|
||||
(event) => {
|
||||
if (event.key !== "Escape" || event.repeat) return;
|
||||
if (this._canTriggerOnEscape()) this.open();
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
_canTriggerOnEscape() {
|
||||
if (this.element && this.element.state !== "closed") return false;
|
||||
if (document.querySelector(".modal-overlay.visible")) return false;
|
||||
if (document.querySelector(".dialog-overlay.visible")) return false;
|
||||
if (document.querySelector(".context-menu.visible")) return false;
|
||||
const mobilePanel = document.getElementById("mobile-panel");
|
||||
if (mobilePanel && mobilePanel.classList.contains("open")) return false;
|
||||
const active = document.activeElement;
|
||||
if (active) {
|
||||
const editable = ["INPUT", "TEXTAREA", "SELECT"].includes(active.tagName);
|
||||
if (editable || active.isContentEditable) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async open() {
|
||||
await this.ready;
|
||||
if (this.element) this.element.open();
|
||||
|
||||
Reference in New Issue
Block a user