mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 10:00:17 +05:30
feat: initialize Tauri 2.x project structure
This commit is contained in:
+5
-1
@@ -24,7 +24,11 @@
|
||||
"dist": "electron-builder --publish=never",
|
||||
"dist:all": "electron-builder -mwl",
|
||||
"download-tools": "node scripts/download-tools.js",
|
||||
"generate-icons": "node scripts/generate-icons.js"
|
||||
"generate-icons": "node scripts/generate-icons.js",
|
||||
"tauri": "tauri",
|
||||
"tauri:dev": "tauri dev",
|
||||
"tauri:build": "tauri build",
|
||||
"tauri:debug": "tauri build --debug"
|
||||
},
|
||||
"keywords": [
|
||||
"markdown",
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
[package]
|
||||
name = "markdown-converter"
|
||||
version = "4.3.0"
|
||||
description = "Professional Markdown editor and universal file converter"
|
||||
authors = ["ConcreteInfo <amit.wh@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "markdown_converter_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = ["devtools", "protocol-asset"] }
|
||||
tauri-plugin-shell = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-fs = "2"
|
||||
tauri-plugin-process = "2"
|
||||
tauri-plugin-clipboard-manager = "2"
|
||||
tauri-plugin-notification = "2"
|
||||
tauri-plugin-log = { version = "2", features = ["colored"] }
|
||||
tauri-plugin-store = "2"
|
||||
tauri-plugin-os = "2"
|
||||
tauri-plugin-opener = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
git2 = "0.19"
|
||||
log = "0.4"
|
||||
walkdir = "2"
|
||||
dirs = "5"
|
||||
chrono = "0.4"
|
||||
thiserror = "1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winreg = "0.52"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
opt-level = "s"
|
||||
strip = true
|
||||
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use std::fs;
|
||||
use tauri::CommandResult;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_app_version() -> String {
|
||||
env!("CARGO_PKG_VERSION").to_string()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_recent_files() -> CommandResult<Vec<String>, AppError> {
|
||||
let config_dir = dirs::config_dir()
|
||||
.ok_or_else(|| AppError::Config("Cannot find config directory".to_string()))?;
|
||||
let recent_path = config_dir.join("markdown-converter").join("recent-files.json");
|
||||
if recent_path.exists() {
|
||||
let content = fs::read_to_string(&recent_path).map_err(|e| AppError::FileRead(e.to_string()))?;
|
||||
let files: Vec<String> = serde_json::from_str(&content).unwrap_or_default();
|
||||
Ok(files)
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn save_recent_files(files: Vec<String>) -> CommandResult<(), AppError> {
|
||||
let config_dir = dirs::config_dir()
|
||||
.ok_or_else(|| AppError::Config("Cannot find config directory".to_string()))?;
|
||||
let app_dir = config_dir.join("markdown-converter");
|
||||
fs::create_dir_all(&app_dir).map_err(|e| AppError::FileWrite(e.to_string()))?;
|
||||
let recent_path = app_dir.join("recent-files.json");
|
||||
let content = serde_json::to_string_pretty(&files).map_err(|e| AppError::Config(e.to_string()))?;
|
||||
fs::write(&recent_path, content).map_err(|e| AppError::FileWrite(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
use tauri::CommandResult;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn open_file_dialog(app: tauri::AppHandle) -> CommandResult<Option<String>, AppError> {
|
||||
let file = app.dialog()
|
||||
.file()
|
||||
.add_filter("Markdown", &["md", "markdown"])
|
||||
.add_filter("All Files", &["*"]);
|
||||
let result = file.blocking_pick_file();
|
||||
Ok(result.map(|p| p.to_string()))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn save_file_dialog(app: tauri::AppHandle, default_name: Option<String>) -> CommandResult<Option<String>, AppError> {
|
||||
let file = app.dialog()
|
||||
.file()
|
||||
.set_file_name(default_name.unwrap_or_else(|| "untitled.md".to_string()));
|
||||
let result = file.blocking_save_file();
|
||||
Ok(result.map(|p| p.to_string()))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_folder_dialog(app: tauri::AppHandle) -> CommandResult<Option<String>, AppError> {
|
||||
let folder = app.dialog().file().blocking_pick_folder();
|
||||
Ok(folder.map(|p| p.to_string()))
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::process::Command;
|
||||
use tauri::CommandResult;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_markdown(
|
||||
input_path: String,
|
||||
output_path: String,
|
||||
format: String,
|
||||
options: ExportOptions,
|
||||
) -> CommandResult<String, AppError> {
|
||||
let mut args = vec![input_path.clone(), "-o".to_string(), output_path.clone()];
|
||||
|
||||
match format.as_str() {
|
||||
"pdf" => {
|
||||
args.push("--pdf-engine=xelatex".to_string());
|
||||
if options.standalone.unwrap_or(false) {
|
||||
args.push("-s".to_string());
|
||||
}
|
||||
},
|
||||
"docx" => {
|
||||
args.push("--to=docx".to_string());
|
||||
if options.standalone.unwrap_or(false) {
|
||||
args.push("-s".to_string());
|
||||
}
|
||||
},
|
||||
"html" => {
|
||||
args.push("--to=html".to_string());
|
||||
if options.standalone.unwrap_or(false) {
|
||||
args.push("-s".to_string());
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
args.push(format!("--to={}", format));
|
||||
}
|
||||
}
|
||||
|
||||
let output = Command::new("pandoc")
|
||||
.args(&args)
|
||||
.output()
|
||||
.map_err(|e| AppError::Export(e.to_string()))?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(AppError::Export(stderr.to_string()));
|
||||
}
|
||||
|
||||
Ok(output_path)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct ExportOptions {
|
||||
pub standalone: Option<bool>,
|
||||
pub template: Option<String>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn check_pandoc_available() -> CommandResult<bool, AppError> {
|
||||
let output = Command::new("pandoc")
|
||||
.arg("--version")
|
||||
.output();
|
||||
Ok(output.is_ok())
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use tauri::CommandResult;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn read_file(path: String) -> CommandResult<String, AppError> {
|
||||
fs::read_to_string(&path).map_err(|e| AppError::FileRead(e.to_string()))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn write_file(path: String, content: String) -> CommandResult<(), AppError> {
|
||||
if let Some(parent) = Path::new(&path).parent() {
|
||||
fs::create_dir_all(parent).map_err(|e| AppError::FileWrite(e.to_string()))?;
|
||||
}
|
||||
fs::write(&path, content).map_err(|e| AppError::FileWrite(e.to_string()))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_file(path: String) -> CommandResult<(), AppError> {
|
||||
fs::remove_file(&path).map_err(|e| AppError::FileDelete(e.to_string()))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn path_exists(path: String) -> bool {
|
||||
Path::new(&path).exists()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn is_directory(path: String) -> bool {
|
||||
Path::new(&path).is_dir()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn list_directory(path: String) -> CommandResult<Vec<FileEntry>, AppError> {
|
||||
let mut entries = Vec::new();
|
||||
for entry in walkdir::WalkDir::new(&path).max_depth(1).into_iter().filter_map(|e| e.ok()) {
|
||||
let p = entry.path();
|
||||
if p == Path::new(&path) { continue; }
|
||||
entries.push(FileEntry {
|
||||
name: p.file_name().unwrap_or_default().to_string_lossy().to_string(),
|
||||
path: p.to_string_lossy().to_string(),
|
||||
is_dir: p.is_dir(),
|
||||
is_file: p.is_file(),
|
||||
});
|
||||
}
|
||||
Ok(entries)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn ensure_directory(path: String) -> CommandResult<(), AppError> {
|
||||
fs::create_dir_all(&path).map_err(|e| AppError::FileWrite(e.to_string()))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn copy_path(source: String, destination: String) -> CommandResult<(), AppError> {
|
||||
fs::copy(&source, &destination).map_err(|e| AppError::FileCopy(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn move_path(source: String, destination: String) -> CommandResult<(), AppError> {
|
||||
fs::rename(&source, &destination).map_err(|e| AppError::FileMove(e.to_string()))
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct FileEntry {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub is_dir: bool,
|
||||
pub is_file: bool,
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
use git2::{Repository, Status, DiffOptions};
|
||||
use tauri::CommandResult;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_status(repo_path: String) -> CommandResult<Vec<GitStatusEntry>, AppError> {
|
||||
let repo = Repository::open(&repo_path).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let mut statuses = Vec::new();
|
||||
|
||||
repo.statuses(None).map_err(|e| AppError::Git(e.to_string()))?.foreach(|entry| {
|
||||
if let Some(status) = entry.status() {
|
||||
let path = entry.path().unwrap_or("").to_string();
|
||||
statuses.push(GitStatusEntry {
|
||||
path,
|
||||
is_staged: status.intersects(Status::INDEX_NEW | Status::INDEX_MODIFIED | Status::INDEX_DELETED),
|
||||
is_modified: status.intersects(Status::WT_MODIFIED),
|
||||
is_new: status.intersects(Status::WT_NEW),
|
||||
is_deleted: status.intersects(Status::WT_DELETED),
|
||||
});
|
||||
}
|
||||
true
|
||||
}).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
|
||||
Ok(statuses)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct GitStatusEntry {
|
||||
pub path: String,
|
||||
pub is_staged: bool,
|
||||
pub is_modified: bool,
|
||||
pub is_new: bool,
|
||||
pub is_deleted: bool,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_stage(repo_path: String, path: String) -> CommandResult<(), AppError> {
|
||||
let repo = Repository::open(&repo_path).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let mut index = repo.index().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
index.add_path(std::path::Path::new(&path)).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
index.write().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_commit(repo_path: String, message: String) -> CommandResult<String, AppError> {
|
||||
let repo = Repository::open(&repo_path).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let mut index = repo.index().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let oid = index.write_tree().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let tree = repo.find_tree(oid).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let signature = repo.signature().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let parent = repo.head().ok().and_then(|h| h.peel_to_commit().ok());
|
||||
let commit = repo.commit(
|
||||
Some("HEAD"), &signature, &signature, &message, &tree,
|
||||
parent.as_ref(),
|
||||
).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
Ok(commit.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_log(repo_path: String, limit: Option<usize>) -> CommandResult<Vec<GitCommitEntry>, AppError> {
|
||||
let repo = Repository::open(&repo_path).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let mut revwalk = repo.revwalk().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
revwalk.push_head().map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let max = limit.unwrap_or(50);
|
||||
let mut commits = Vec::new();
|
||||
for (i, oid) in revwalk.enumerate() {
|
||||
if i >= max { break; }
|
||||
let commit = repo.find_commit(oid).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
commits.push(GitCommitEntry {
|
||||
hash: commit.id().to_string(),
|
||||
message: commit.message().unwrap_or("").to_string(),
|
||||
author: commit.author().name().unwrap_or("").to_string(),
|
||||
date: chrono::DateTime::from_timestamp(commit.time().seconds(), 0)
|
||||
.map(|dt| dt.format("%Y-%m-%d %H:%M:%S").to_string())
|
||||
.unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
Ok(commits)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct GitCommitEntry {
|
||||
pub hash: String,
|
||||
pub message: String,
|
||||
pub author: String,
|
||||
pub date: String,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn git_diff(repo_path: String, path: Option<String>) -> CommandResult<String, AppError> {
|
||||
let repo = Repository::open(&repo_path).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let mut opts = DiffOptions::new();
|
||||
if let Some(p) = path {
|
||||
opts.path_filter(&p);
|
||||
}
|
||||
let diff = repo.diff_index_to_workdir(None, Some(&mut opts))
|
||||
.map_err(|e| AppError::Git(e.to_string()))?;
|
||||
let mut out = Vec::new();
|
||||
diff.print(git2::DiffFormat::Patch, |_delta, _origin, line| {
|
||||
let prefix = match line.origin() {
|
||||
'+' => " +",
|
||||
'-' => " -",
|
||||
' ' => " ",
|
||||
_ => "",
|
||||
};
|
||||
if let Ok(content) = std::str::from_utf8(line.content()) {
|
||||
out.push(format!("{}{}", prefix, content));
|
||||
}
|
||||
true
|
||||
}).map_err(|e| AppError::Git(e.to_string()))?;
|
||||
Ok(out.join(""))
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod file;
|
||||
pub mod export;
|
||||
pub mod git;
|
||||
pub mod app;
|
||||
pub mod pdf;
|
||||
pub mod dialog;
|
||||
@@ -0,0 +1,28 @@
|
||||
use tauri::CommandResult;
|
||||
use crate::error::AppError;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_pdf_page_count(path: String) -> CommandResult<usize, AppError> {
|
||||
// Placeholder — actual implementation in pdf_ops.rs
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn merge_pdfs(paths: Vec<String>, output: String) -> CommandResult<String, AppError> {
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn split_pdf(input: String, output_dir: String, pages_per_split: usize) -> CommandResult<Vec<String>, AppError> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn rotate_pdf(input: String, output: String, degrees: i32) -> CommandResult<(), AppError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn delete_pdf_pages(input: String, output: String, pages: Vec<usize>) -> CommandResult<(), AppError> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AppError {
|
||||
#[error("File read error: {0}")]
|
||||
FileRead(String),
|
||||
#[error("File write error: {0}")]
|
||||
FileWrite(String),
|
||||
#[error("File delete error: {0}")]
|
||||
FileDelete(String),
|
||||
#[error("File copy error: {0}")]
|
||||
FileCopy(String),
|
||||
#[error("File move error: {0}")]
|
||||
FileMove(String),
|
||||
#[error("Export error: {0}")]
|
||||
Export(String),
|
||||
#[error("Git error: {0}")]
|
||||
Git(String),
|
||||
#[error("PDF error: {0}")]
|
||||
Pdf(String),
|
||||
#[error("Config error: {0}")]
|
||||
Config(String),
|
||||
}
|
||||
|
||||
impl serde::Serialize for AppError {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_ref())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_log::Builder::new().build())
|
||||
.plugin(tauri_plugin_store::Builder::new().build())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_fs::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
crate::commands::file::read_file,
|
||||
crate::commands::file::write_file,
|
||||
crate::commands::file::delete_file,
|
||||
crate::commands::file::path_exists,
|
||||
crate::commands::file::is_directory,
|
||||
crate::commands::file::list_directory,
|
||||
crate::commands::file::ensure_directory,
|
||||
crate::commands::file::copy_path,
|
||||
crate::commands::file::move_path,
|
||||
crate::commands::export::export_markdown,
|
||||
crate::commands::export::check_pandoc_available,
|
||||
crate::commands::git::git_status,
|
||||
crate::commands::git::git_stage,
|
||||
crate::commands::git::git_commit,
|
||||
crate::commands::git::git_log,
|
||||
crate::commands::git::git_diff,
|
||||
crate::commands::app::get_app_version,
|
||||
crate::commands::app::get_recent_files,
|
||||
crate::commands::app::save_recent_files,
|
||||
crate::commands::pdf::get_pdf_page_count,
|
||||
crate::commands::pdf::merge_pdfs,
|
||||
crate::commands::pdf::split_pdf,
|
||||
crate::commands::pdf::rotate_pdf,
|
||||
crate::commands::pdf::delete_pdf_pages,
|
||||
crate::commands::dialog::open_file_dialog,
|
||||
crate::commands::dialog::save_file_dialog,
|
||||
crate::commands::dialog::select_folder_dialog,
|
||||
])
|
||||
.setup(|app| {
|
||||
log::info!("MarkdownConverter starting up");
|
||||
Ok(())
|
||||
})
|
||||
.on_window_event(|window, event| {
|
||||
if let tauri::WindowEvent::CloseRequested { .. } = event {
|
||||
let _ = window.emit("file-save", ());
|
||||
}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
markdown_converter_lib::run()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "MarkdownConverter",
|
||||
"version": "4.3.0",
|
||||
"identifier": "com.concreteinfo.markdownconverter",
|
||||
"build": {
|
||||
"devUrl": "http://localhost:1420",
|
||||
"frontendDist": "../dist",
|
||||
"devtools": true
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": true,
|
||||
"windows": [
|
||||
{
|
||||
"title": "MarkdownConverter",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"minWidth": 800,
|
||||
"minHeight": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false,
|
||||
"center": true,
|
||||
"fileDropEnabled": true
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://kroki.io https://plantuml.com"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["nsis", "msi"],
|
||||
"icon": [
|
||||
"../assets/icons/32x32.png",
|
||||
"../assets/icons/128x128.png",
|
||||
"../assets/icons/128x128@2x.png",
|
||||
"../assets/icons/icon.icns",
|
||||
"../assets/icons/icon.ico"
|
||||
],
|
||||
"resources": {},
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"shell": {
|
||||
"open": true,
|
||||
"scope": [
|
||||
{ "name": "pandoc", "cmd": "pandoc", "args": true },
|
||||
{ "name": "ffmpeg", "cmd": "ffmpeg", "args": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user