mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
feat: Add Document Converter component and document conversion helpers
This commit is contained in:
@@ -482,6 +482,263 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "DocumentConverter",
|
||||
"type": "card",
|
||||
"props": {
|
||||
"sx": { "maxWidth": 600, "mx": "auto", "mt": 3 }
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "cardContent",
|
||||
"children": [
|
||||
{
|
||||
"type": "typography",
|
||||
"props": { "variant": "h5", "gutterBottom": true },
|
||||
"children": "Document Converter"
|
||||
},
|
||||
{
|
||||
"type": "typography",
|
||||
"props": { "variant": "body2", "color": "text.secondary", "paragraph": true },
|
||||
"children": "Convert Markdown, HTML, and other documents to PDF, DOCX, EPUB, and more."
|
||||
},
|
||||
{
|
||||
"type": "box",
|
||||
"props": { "component": "form", "sx": { "display": "flex", "flexDirection": "column", "gap": 2 } },
|
||||
"children": [
|
||||
{
|
||||
"type": "textField",
|
||||
"props": {
|
||||
"label": "Input File",
|
||||
"placeholder": "/path/to/document.md",
|
||||
"fullWidth": true,
|
||||
"required": true
|
||||
},
|
||||
"binding": "form.input_path"
|
||||
},
|
||||
{
|
||||
"type": "grid",
|
||||
"props": { "container": true, "spacing": 2 },
|
||||
"children": [
|
||||
{
|
||||
"type": "grid",
|
||||
"props": { "item": true, "xs": 6 },
|
||||
"children": [
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Input Format",
|
||||
"fullWidth": true,
|
||||
"options": [
|
||||
{ "value": "markdown", "label": "Markdown" },
|
||||
{ "value": "html", "label": "HTML" },
|
||||
{ "value": "docx", "label": "Word (DOCX)" },
|
||||
{ "value": "latex", "label": "LaTeX" },
|
||||
{ "value": "rst", "label": "reStructuredText" },
|
||||
{ "value": "org", "label": "Org-mode" }
|
||||
]
|
||||
},
|
||||
"binding": "form.input_format"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "grid",
|
||||
"props": { "item": true, "xs": 6 },
|
||||
"children": [
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Output Format",
|
||||
"fullWidth": true,
|
||||
"options": [
|
||||
{ "value": "pdf", "label": "PDF" },
|
||||
{ "value": "html", "label": "HTML" },
|
||||
{ "value": "docx", "label": "Word (DOCX)" },
|
||||
{ "value": "epub", "label": "EPUB" },
|
||||
{ "value": "odt", "label": "OpenDocument" },
|
||||
{ "value": "latex", "label": "LaTeX" }
|
||||
]
|
||||
},
|
||||
"binding": "form.output_format"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "textField",
|
||||
"props": {
|
||||
"label": "Output Path (optional)",
|
||||
"placeholder": "Auto-generated if empty",
|
||||
"fullWidth": true
|
||||
},
|
||||
"binding": "form.output_path"
|
||||
},
|
||||
{
|
||||
"type": "accordion",
|
||||
"children": [
|
||||
{
|
||||
"type": "accordionSummary",
|
||||
"props": { "expandIcon": "ExpandMore" },
|
||||
"children": [
|
||||
{
|
||||
"type": "typography",
|
||||
"children": "Advanced Options"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "accordionDetails",
|
||||
"children": [
|
||||
{
|
||||
"type": "box",
|
||||
"props": { "sx": { "display": "flex", "flexDirection": "column", "gap": 2 } },
|
||||
"children": [
|
||||
{
|
||||
"type": "textField",
|
||||
"props": { "label": "Document Title", "fullWidth": true },
|
||||
"binding": "form.title"
|
||||
},
|
||||
{
|
||||
"type": "textField",
|
||||
"props": { "label": "Author", "fullWidth": true },
|
||||
"binding": "form.author"
|
||||
},
|
||||
{
|
||||
"type": "formControlLabel",
|
||||
"props": {
|
||||
"label": "Include Table of Contents",
|
||||
"control": { "type": "checkbox" }
|
||||
},
|
||||
"binding": "form.toc"
|
||||
},
|
||||
{
|
||||
"type": "grid",
|
||||
"props": { "container": true, "spacing": 2 },
|
||||
"children": [
|
||||
{
|
||||
"type": "grid",
|
||||
"props": { "item": true, "xs": 6 },
|
||||
"children": [
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Paper Size",
|
||||
"fullWidth": true,
|
||||
"options": [
|
||||
{ "value": "a4", "label": "A4" },
|
||||
{ "value": "letter", "label": "Letter" },
|
||||
{ "value": "legal", "label": "Legal" }
|
||||
]
|
||||
},
|
||||
"binding": "form.paper_size"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "grid",
|
||||
"props": { "item": true, "xs": 6 },
|
||||
"children": [
|
||||
{
|
||||
"type": "textField",
|
||||
"props": { "label": "Margin", "placeholder": "1in", "fullWidth": true },
|
||||
"binding": "form.margin"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Code Highlight Style",
|
||||
"fullWidth": true,
|
||||
"options": [
|
||||
{ "value": "tango", "label": "Tango" },
|
||||
{ "value": "pygments", "label": "Pygments" },
|
||||
{ "value": "kate", "label": "Kate" },
|
||||
{ "value": "monochrome", "label": "Monochrome" },
|
||||
{ "value": "espresso", "label": "Espresso" },
|
||||
{ "value": "zenburn", "label": "Zenburn" }
|
||||
]
|
||||
},
|
||||
"binding": "form.highlight_style"
|
||||
},
|
||||
{
|
||||
"type": "textField",
|
||||
"props": { "label": "Custom Template Path", "fullWidth": true },
|
||||
"binding": "form.template"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"props": {
|
||||
"variant": "contained",
|
||||
"color": "primary",
|
||||
"size": "large",
|
||||
"fullWidth": true,
|
||||
"startIcon": { "type": "icon", "props": { "name": "Transform" } }
|
||||
},
|
||||
"events": {
|
||||
"onClick": "document_helpers.convert(form.input_path, form.output_format, form)"
|
||||
},
|
||||
"children": "Convert Document"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "QuickConvertButtons",
|
||||
"type": "box",
|
||||
"props": { "sx": { "display": "flex", "gap": 2, "flexWrap": "wrap" } },
|
||||
"children": [
|
||||
{
|
||||
"type": "button",
|
||||
"props": {
|
||||
"variant": "outlined",
|
||||
"startIcon": { "type": "icon", "props": { "name": "PictureAsPdf" } }
|
||||
},
|
||||
"events": { "onClick": "openDialog('DocumentConverter', { output_format: 'pdf' })" },
|
||||
"children": "Markdown → PDF"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"props": {
|
||||
"variant": "outlined",
|
||||
"startIcon": { "type": "icon", "props": { "name": "Code" } }
|
||||
},
|
||||
"events": { "onClick": "openDialog('DocumentConverter', { output_format: 'html' })" },
|
||||
"children": "Markdown → HTML"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"props": {
|
||||
"variant": "outlined",
|
||||
"startIcon": { "type": "icon", "props": { "name": "Article" } }
|
||||
},
|
||||
"events": { "onClick": "openDialog('DocumentConverter', { output_format: 'docx' })" },
|
||||
"children": "Markdown → DOCX"
|
||||
},
|
||||
{
|
||||
"type": "button",
|
||||
"props": {
|
||||
"variant": "outlined",
|
||||
"startIcon": { "type": "icon", "props": { "name": "MenuBook" } }
|
||||
},
|
||||
"events": { "onClick": "openDialog('DocumentConverter', { output_format: 'epub' })" },
|
||||
"children": "Markdown → EPUB"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"id": "media_center",
|
||||
"name": "Media Center",
|
||||
"version": "1.0.0",
|
||||
"description": "Media processing dashboard with job queue, radio, and TV channel management",
|
||||
"description": "Media processing dashboard with job queue, radio, TV channel management, and document conversion",
|
||||
"author": "MetaBuilder",
|
||||
"minLevel": 3,
|
||||
"tags": ["media", "streaming", "radio", "tv", "transcoding"],
|
||||
"tags": ["media", "streaming", "radio", "tv", "transcoding", "documents", "pdf"],
|
||||
"dependencies": ["notification_center", "data_table"],
|
||||
"exports": {
|
||||
"components": [
|
||||
@@ -19,19 +19,23 @@
|
||||
"TvChannelEditor",
|
||||
"TvScheduleEditor",
|
||||
"TvEpgViewer",
|
||||
"MediaPlayer"
|
||||
"MediaPlayer",
|
||||
"DocumentConverter",
|
||||
"QuickConvertButtons"
|
||||
],
|
||||
"pages": [
|
||||
"MediaOverviewPage",
|
||||
"JobsPage",
|
||||
"RadioPage",
|
||||
"TvPage"
|
||||
"TvPage",
|
||||
"DocumentsPage"
|
||||
],
|
||||
"scripts": [
|
||||
"media_api",
|
||||
"job_helpers",
|
||||
"radio_helpers",
|
||||
"tv_helpers"
|
||||
"tv_helpers",
|
||||
"document_helpers"
|
||||
]
|
||||
},
|
||||
"routes": [
|
||||
|
||||
277
packages/media_center/seed/scripts/lua/document_helpers.lua
Normal file
277
packages/media_center/seed/scripts/lua/document_helpers.lua
Normal file
@@ -0,0 +1,277 @@
|
||||
---@alias DocumentFormat "md" | "markdown" | "html" | "docx" | "pdf" | "latex" | "epub" | "odt" | "rst" | "txt"
|
||||
|
||||
---@class DocumentConvertOptions
|
||||
---@field toc? boolean Include table of contents
|
||||
---@field template? string Custom template path
|
||||
---@field title? string Document title
|
||||
---@field author? string Document author
|
||||
---@field date? string Document date
|
||||
---@field paper_size? "a4" | "letter" | "legal" Paper size for PDF
|
||||
---@field margin? string Margin (e.g., "1in", "2cm")
|
||||
---@field highlight_style? string Code highlight style
|
||||
|
||||
---@class DocumentJob
|
||||
---@field id string Job ID
|
||||
---@field status string Job status
|
||||
---@field progress? number Progress 0-100
|
||||
---@field output_path? string Output file path
|
||||
---@field error? string Error message
|
||||
|
||||
---@class DocumentHelpersModule
|
||||
---@field convert fun(input_path: string, output_format: DocumentFormat, options?: DocumentConvertOptions): DocumentJob|nil, string?
|
||||
---@field markdown_to_pdf fun(input_path: string, output_path?: string, options?: DocumentConvertOptions): DocumentJob|nil, string?
|
||||
---@field markdown_to_html fun(input_path: string, output_path?: string, options?: DocumentConvertOptions): DocumentJob|nil, string?
|
||||
---@field markdown_to_docx fun(input_path: string, output_path?: string, options?: DocumentConvertOptions): DocumentJob|nil, string?
|
||||
---@field get_supported_formats fun(): table
|
||||
local M = {}
|
||||
|
||||
local config = {
|
||||
base_url = "http://localhost:8090"
|
||||
}
|
||||
|
||||
---Configure the helper
|
||||
---@param opts table Configuration options
|
||||
function M.configure(opts)
|
||||
if opts.base_url then config.base_url = opts.base_url end
|
||||
end
|
||||
|
||||
---Get supported input formats
|
||||
---@return string[] formats List of input formats
|
||||
function M.get_input_formats()
|
||||
return {
|
||||
"md", "markdown", "gfm", "commonmark",
|
||||
"html", "htm",
|
||||
"tex", "latex",
|
||||
"docx", "odt",
|
||||
"rst", "org", "txt",
|
||||
"json", "yaml"
|
||||
}
|
||||
end
|
||||
|
||||
---Get supported output formats
|
||||
---@return string[] formats List of output formats
|
||||
function M.get_output_formats()
|
||||
return {
|
||||
"pdf", "html", "html5",
|
||||
"docx", "odt", "rtf",
|
||||
"epub", "epub3",
|
||||
"latex", "beamer",
|
||||
"markdown", "gfm",
|
||||
"plain", "json"
|
||||
}
|
||||
end
|
||||
|
||||
---Get supported format conversions as a table
|
||||
---@return table formats { input = {...}, output = {...} }
|
||||
function M.get_supported_formats()
|
||||
return {
|
||||
input = M.get_input_formats(),
|
||||
output = M.get_output_formats()
|
||||
}
|
||||
end
|
||||
|
||||
---Check if a conversion is supported
|
||||
---@param from_format DocumentFormat Input format
|
||||
---@param to_format DocumentFormat Output format
|
||||
---@return boolean supported Whether conversion is supported
|
||||
function M.supports_conversion(from_format, to_format)
|
||||
local input_formats = M.get_input_formats()
|
||||
local output_formats = M.get_output_formats()
|
||||
|
||||
local input_ok = false
|
||||
for _, fmt in ipairs(input_formats) do
|
||||
if fmt == from_format then
|
||||
input_ok = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local output_ok = false
|
||||
for _, fmt in ipairs(output_formats) do
|
||||
if fmt == to_format then
|
||||
output_ok = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return input_ok and output_ok
|
||||
end
|
||||
|
||||
---Generate output path from input path
|
||||
---@param input_path string Input file path
|
||||
---@param output_format string Output format extension
|
||||
---@return string output_path Generated output path
|
||||
local function generate_output_path(input_path, output_format)
|
||||
-- Remove extension and add new one
|
||||
local base = input_path:match("(.+)%.[^%.]+$") or input_path
|
||||
return base .. "." .. output_format
|
||||
end
|
||||
|
||||
---Convert a document
|
||||
---@param input_path string Path to input file
|
||||
---@param output_format DocumentFormat Target format
|
||||
---@param options? DocumentConvertOptions Conversion options
|
||||
---@return DocumentJob|nil job Job info if successful
|
||||
---@return string? error Error message if failed
|
||||
function M.convert(input_path, output_format, options)
|
||||
if not input_path or input_path == "" then
|
||||
return nil, "Input path is required"
|
||||
end
|
||||
|
||||
if not output_format or output_format == "" then
|
||||
return nil, "Output format is required"
|
||||
end
|
||||
|
||||
options = options or {}
|
||||
|
||||
local output_path = options.output_path or generate_output_path(input_path, output_format)
|
||||
|
||||
-- Build request body
|
||||
local body = {
|
||||
type = "document_convert",
|
||||
params = {
|
||||
input_path = input_path,
|
||||
output_path = output_path,
|
||||
output_format = output_format,
|
||||
variables = {}
|
||||
},
|
||||
notify_user = true
|
||||
}
|
||||
|
||||
-- Add options as variables
|
||||
if options.toc then
|
||||
body.params.variables.toc = "true"
|
||||
end
|
||||
if options.template then
|
||||
body.params.template_path = options.template
|
||||
end
|
||||
if options.title then
|
||||
body.params.variables.title = options.title
|
||||
end
|
||||
if options.author then
|
||||
body.params.variables.author = options.author
|
||||
end
|
||||
if options.date then
|
||||
body.params.variables.date = options.date
|
||||
end
|
||||
if options.paper_size then
|
||||
body.params.variables.papersize = options.paper_size
|
||||
end
|
||||
if options.margin then
|
||||
body.params.variables["geometry:margin"] = options.margin
|
||||
end
|
||||
|
||||
-- HTTP POST /api/jobs would go here
|
||||
-- Return mock response for now
|
||||
return {
|
||||
id = "doc_" .. tostring(os.time()),
|
||||
status = "pending",
|
||||
progress = 0
|
||||
}
|
||||
end
|
||||
|
||||
---Convert Markdown to PDF
|
||||
---@param input_path string Path to markdown file
|
||||
---@param output_path? string Path for PDF output (auto-generated if nil)
|
||||
---@param options? DocumentConvertOptions Conversion options
|
||||
---@return DocumentJob|nil job Job info if successful
|
||||
---@return string? error Error message if failed
|
||||
function M.markdown_to_pdf(input_path, output_path, options)
|
||||
options = options or {}
|
||||
options.output_path = output_path
|
||||
return M.convert(input_path, "pdf", options)
|
||||
end
|
||||
|
||||
---Convert Markdown to HTML
|
||||
---@param input_path string Path to markdown file
|
||||
---@param output_path? string Path for HTML output (auto-generated if nil)
|
||||
---@param options? DocumentConvertOptions Conversion options
|
||||
---@return DocumentJob|nil job Job info if successful
|
||||
---@return string? error Error message if failed
|
||||
function M.markdown_to_html(input_path, output_path, options)
|
||||
options = options or {}
|
||||
options.output_path = output_path
|
||||
return M.convert(input_path, "html", options)
|
||||
end
|
||||
|
||||
---Convert Markdown to DOCX
|
||||
---@param input_path string Path to markdown file
|
||||
---@param output_path? string Path for DOCX output (auto-generated if nil)
|
||||
---@param options? DocumentConvertOptions Conversion options
|
||||
---@return DocumentJob|nil job Job info if successful
|
||||
---@return string? error Error message if failed
|
||||
function M.markdown_to_docx(input_path, output_path, options)
|
||||
options = options or {}
|
||||
options.output_path = output_path
|
||||
return M.convert(input_path, "docx", options)
|
||||
end
|
||||
|
||||
---Convert HTML to PDF
|
||||
---@param input_path string Path to HTML file
|
||||
---@param output_path? string Path for PDF output
|
||||
---@param options? DocumentConvertOptions Conversion options
|
||||
---@return DocumentJob|nil job Job info if successful
|
||||
---@return string? error Error message if failed
|
||||
function M.html_to_pdf(input_path, output_path, options)
|
||||
options = options or {}
|
||||
options.output_path = output_path
|
||||
return M.convert(input_path, "pdf", options)
|
||||
end
|
||||
|
||||
---Convert DOCX to PDF
|
||||
---@param input_path string Path to DOCX file
|
||||
---@param output_path? string Path for PDF output
|
||||
---@param options? DocumentConvertOptions Conversion options
|
||||
---@return DocumentJob|nil job Job info if successful
|
||||
---@return string? error Error message if failed
|
||||
function M.docx_to_pdf(input_path, output_path, options)
|
||||
options = options or {}
|
||||
options.output_path = output_path
|
||||
return M.convert(input_path, "pdf", options)
|
||||
end
|
||||
|
||||
---Get format display name
|
||||
---@param format string Format code
|
||||
---@return string name Display name
|
||||
function M.get_format_name(format)
|
||||
local names = {
|
||||
md = "Markdown",
|
||||
markdown = "Markdown",
|
||||
gfm = "GitHub Flavored Markdown",
|
||||
html = "HTML",
|
||||
htm = "HTML",
|
||||
pdf = "PDF",
|
||||
docx = "Word Document",
|
||||
odt = "OpenDocument Text",
|
||||
rtf = "Rich Text Format",
|
||||
epub = "EPUB",
|
||||
latex = "LaTeX",
|
||||
tex = "LaTeX",
|
||||
rst = "reStructuredText",
|
||||
org = "Org-mode",
|
||||
txt = "Plain Text",
|
||||
json = "JSON",
|
||||
yaml = "YAML"
|
||||
}
|
||||
return names[format] or format:upper()
|
||||
end
|
||||
|
||||
---Get format icon name (for UI)
|
||||
---@param format string Format code
|
||||
---@return string icon MUI icon name
|
||||
function M.get_format_icon(format)
|
||||
local icons = {
|
||||
md = "Description",
|
||||
markdown = "Description",
|
||||
html = "Code",
|
||||
pdf = "PictureAsPdf",
|
||||
docx = "Article",
|
||||
odt = "Article",
|
||||
epub = "MenuBook",
|
||||
latex = "Functions",
|
||||
tex = "Functions"
|
||||
}
|
||||
return icons[format] or "InsertDriveFile"
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user