markpdfdown 0.4.2 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { app, ipcMain, dialog, shell, safeStorage, protocol, nativeImage, BrowserWindow } from "electron";
1
+ import { app, ipcMain, dialog, clipboard, nativeImage, shell, safeStorage, protocol, BrowserWindow } from "electron";
2
2
  import path from "path";
3
3
  import fs, { promises } from "fs";
4
4
  import isDev from "electron-is-dev";
@@ -1653,7 +1653,14 @@ class ConverterWorker extends WorkerBase {
1653
1653
  }
1654
1654
  const task = await tx.task.findUnique({
1655
1655
  where: { id: page.task },
1656
- select: { status: true, pages: true, completed_count: true, failed_count: true }
1656
+ select: {
1657
+ status: true,
1658
+ pages: true,
1659
+ completed_count: true,
1660
+ failed_count: true,
1661
+ provider: true,
1662
+ model: true
1663
+ }
1657
1664
  });
1658
1665
  if (!task) {
1659
1666
  throw new Error("Task not found");
@@ -1672,7 +1679,9 @@ class ConverterWorker extends WorkerBase {
1672
1679
  completed_at: /* @__PURE__ */ new Date(),
1673
1680
  worker_id: null,
1674
1681
  // Release worker
1675
- error: null
1682
+ error: null,
1683
+ provider: task.provider,
1684
+ model: task.model
1676
1685
  }
1677
1686
  });
1678
1687
  const updatedTask = await tx.task.update({
@@ -1737,7 +1746,14 @@ class ConverterWorker extends WorkerBase {
1737
1746
  }
1738
1747
  const task = await tx.task.findUnique({
1739
1748
  where: { id: page.task },
1740
- select: { status: true, pages: true, completed_count: true, failed_count: true }
1749
+ select: {
1750
+ status: true,
1751
+ pages: true,
1752
+ completed_count: true,
1753
+ failed_count: true,
1754
+ provider: true,
1755
+ model: true
1756
+ }
1741
1757
  });
1742
1758
  if (!task) {
1743
1759
  throw new Error("Task not found");
@@ -1751,8 +1767,10 @@ class ConverterWorker extends WorkerBase {
1751
1767
  status: PageStatus.FAILED,
1752
1768
  error: errorMessage,
1753
1769
  completed_at: /* @__PURE__ */ new Date(),
1754
- worker_id: null
1770
+ worker_id: null,
1755
1771
  // Release worker
1772
+ provider: task.provider,
1773
+ model: task.model
1756
1774
  }
1757
1775
  });
1758
1776
  const updatedTask = await tx.task.update({
@@ -2620,6 +2638,7 @@ const IPC_CHANNELS = {
2620
2638
  GET_ALL: "task:getAll",
2621
2639
  GET_BY_ID: "task:getById",
2622
2640
  UPDATE: "task:update",
2641
+ RETRY: "task:retry",
2623
2642
  DELETE: "task:delete",
2624
2643
  HAS_RUNNING: "task:hasRunningTasks"
2625
2644
  },
@@ -2635,6 +2654,7 @@ const IPC_CHANNELS = {
2635
2654
  FILE: {
2636
2655
  GET_IMAGE_PATH: "file:getImagePath",
2637
2656
  DOWNLOAD_MARKDOWN: "file:downloadMarkdown",
2657
+ COPY_IMAGE_TO_CLIPBOARD: "file:copyImageToClipboard",
2638
2658
  SELECT_DIALOG: "file:selectDialog",
2639
2659
  UPLOAD: "file:upload",
2640
2660
  UPLOAD_FILE_CONTENT: "file:uploadFileContent"
@@ -3122,6 +3142,123 @@ function registerTaskHandlers() {
3122
3142
  }
3123
3143
  }
3124
3144
  );
3145
+ ipcMain.handle(
3146
+ IPC_CHANNELS.TASK.RETRY,
3147
+ async (_, params) => {
3148
+ try {
3149
+ const payload = typeof params === "string" ? { taskId: params } : params;
3150
+ const taskId = payload?.taskId;
3151
+ if (!taskId) {
3152
+ return { success: false, error: "Task ID is required" };
3153
+ }
3154
+ const hasProviderOverride = payload?.providerId !== void 0;
3155
+ const hasModelOverride = payload?.modelId !== void 0;
3156
+ const hasAnyModelOverride = hasProviderOverride || hasModelOverride;
3157
+ if (hasAnyModelOverride && (!hasProviderOverride || !hasModelOverride)) {
3158
+ return { success: false, error: "providerId and modelId must be provided together" };
3159
+ }
3160
+ const updatedTask = await prisma.$transaction(async (tx) => {
3161
+ const task = await tx.task.findUnique({
3162
+ where: { id: taskId }
3163
+ });
3164
+ if (!task) {
3165
+ throw new Error("Task not found");
3166
+ }
3167
+ const retryableStatuses = [
3168
+ TaskStatus.FAILED,
3169
+ TaskStatus.COMPLETED,
3170
+ TaskStatus.CANCELLED,
3171
+ TaskStatus.PARTIAL_FAILED
3172
+ ];
3173
+ if (!retryableStatuses.includes(task.status)) {
3174
+ throw new Error("Task is not retryable");
3175
+ }
3176
+ let targetProvider = task.provider;
3177
+ let targetModel = task.model;
3178
+ let targetModelName = task.model_name;
3179
+ if (hasAnyModelOverride) {
3180
+ const providerId = payload.providerId;
3181
+ const modelId = payload.modelId;
3182
+ const provider = await tx.provider.findUnique({
3183
+ where: { id: providerId },
3184
+ select: { id: true, name: true, status: true }
3185
+ });
3186
+ if (!provider || provider.status !== 0) {
3187
+ throw new Error("Provider not found or disabled");
3188
+ }
3189
+ const model = await tx.model.findUnique({
3190
+ where: {
3191
+ id_provider: {
3192
+ id: modelId,
3193
+ provider: providerId
3194
+ }
3195
+ },
3196
+ select: { id: true, name: true, provider: true }
3197
+ });
3198
+ if (!model) {
3199
+ throw new Error("Model not found for provider");
3200
+ }
3201
+ targetProvider = providerId;
3202
+ targetModel = modelId;
3203
+ targetModelName = `${model.name} | ${provider.name}`;
3204
+ }
3205
+ const detailCount = await tx.taskDetail.count({
3206
+ where: { task: taskId }
3207
+ });
3208
+ if (detailCount > 0) {
3209
+ await tx.taskDetail.updateMany({
3210
+ where: { task: taskId },
3211
+ data: {
3212
+ status: PageStatus.PENDING,
3213
+ retry_count: 0,
3214
+ error: null,
3215
+ worker_id: null,
3216
+ started_at: null,
3217
+ completed_at: null,
3218
+ input_tokens: 0,
3219
+ output_tokens: 0,
3220
+ conversion_time: 0,
3221
+ content: "",
3222
+ provider: targetProvider,
3223
+ model: targetModel
3224
+ }
3225
+ });
3226
+ }
3227
+ return await tx.task.update({
3228
+ where: { id: taskId },
3229
+ data: {
3230
+ provider: targetProvider,
3231
+ model: targetModel,
3232
+ model_name: targetModelName,
3233
+ status: detailCount > 0 ? TaskStatus.PROCESSING : TaskStatus.PENDING,
3234
+ progress: 0,
3235
+ completed_count: 0,
3236
+ failed_count: 0,
3237
+ error: null,
3238
+ merged_path: null,
3239
+ worker_id: null
3240
+ }
3241
+ });
3242
+ }, {
3243
+ isolationLevel: "Serializable"
3244
+ });
3245
+ eventBus.emitTaskEvent(TaskEventType.TASK_UPDATED, {
3246
+ taskId,
3247
+ task: updatedTask,
3248
+ timestamp: Date.now()
3249
+ });
3250
+ eventBus.emitTaskEvent(TaskEventType.TASK_STATUS_CHANGED, {
3251
+ taskId,
3252
+ task: { status: updatedTask.status },
3253
+ timestamp: Date.now()
3254
+ });
3255
+ return { success: true, data: updatedTask };
3256
+ } catch (error) {
3257
+ console.error("[IPC] task:retry error:", error);
3258
+ return { success: false, error: error.message };
3259
+ }
3260
+ }
3261
+ );
3125
3262
  ipcMain.handle(IPC_CHANNELS.TASK.DELETE, async (_, id) => {
3126
3263
  try {
3127
3264
  await fileLogic.deleteTaskFiles(id);
@@ -3232,11 +3369,19 @@ function registerTaskDetailHandlers() {
3232
3369
  );
3233
3370
  ipcMain.handle(
3234
3371
  IPC_CHANNELS.TASK_DETAIL.RETRY,
3235
- async (_, pageId) => {
3372
+ async (_, params) => {
3236
3373
  try {
3374
+ const payload = typeof params === "number" ? { pageId: params } : params;
3375
+ const pageId = payload?.pageId;
3237
3376
  if (!pageId) {
3238
3377
  return { success: false, error: "Page ID is required" };
3239
3378
  }
3379
+ const hasProviderOverride = payload?.providerId !== void 0;
3380
+ const hasModelOverride = payload?.modelId !== void 0;
3381
+ const hasAnyModelOverride = hasProviderOverride || hasModelOverride;
3382
+ if (hasAnyModelOverride && (!hasProviderOverride || !hasModelOverride)) {
3383
+ return { success: false, error: "providerId and modelId must be provided together" };
3384
+ }
3240
3385
  const result = await prisma.$transaction(async (tx) => {
3241
3386
  const page = await tx.taskDetail.findUnique({
3242
3387
  where: { id: pageId }
@@ -3256,6 +3401,33 @@ function registerTaskDetailHandlers() {
3256
3401
  if (task.status === TaskStatus.CANCELLED) {
3257
3402
  throw new Error("Task is cancelled, cannot retry");
3258
3403
  }
3404
+ let targetProvider = page.provider;
3405
+ let targetModel = page.model;
3406
+ if (hasAnyModelOverride) {
3407
+ const providerId = payload.providerId;
3408
+ const modelId = payload.modelId;
3409
+ const provider = await tx.provider.findUnique({
3410
+ where: { id: providerId },
3411
+ select: { id: true, status: true }
3412
+ });
3413
+ if (!provider || provider.status !== 0) {
3414
+ throw new Error("Provider not found or disabled");
3415
+ }
3416
+ const model = await tx.model.findUnique({
3417
+ where: {
3418
+ id_provider: {
3419
+ id: modelId,
3420
+ provider: providerId
3421
+ }
3422
+ },
3423
+ select: { id: true }
3424
+ });
3425
+ if (!model) {
3426
+ throw new Error("Model not found for provider");
3427
+ }
3428
+ targetProvider = providerId;
3429
+ targetModel = modelId;
3430
+ }
3259
3431
  const updatedPage = await tx.taskDetail.update({
3260
3432
  where: { id: pageId },
3261
3433
  data: {
@@ -3268,7 +3440,9 @@ function registerTaskDetailHandlers() {
3268
3440
  input_tokens: 0,
3269
3441
  output_tokens: 0,
3270
3442
  conversion_time: 0,
3271
- content: ""
3443
+ content: "",
3444
+ provider: targetProvider,
3445
+ model: targetModel
3272
3446
  }
3273
3447
  });
3274
3448
  const decrementField = page.status === PageStatus.FAILED ? "failed_count" : "completed_count";
@@ -3444,6 +3618,20 @@ function registerTaskDetailHandlers() {
3444
3618
  );
3445
3619
  console.log("[IPC] TaskDetail handlers registered");
3446
3620
  }
3621
+ async function createImageFromSource(imageSource) {
3622
+ const normalizedSource = imageSource.trim();
3623
+ const lowerSource = normalizedSource.toLowerCase();
3624
+ if (lowerSource.startsWith("data:image/")) {
3625
+ return nativeImage.createFromDataURL(normalizedSource);
3626
+ }
3627
+ if (lowerSource.startsWith("http://") || lowerSource.startsWith("https://")) {
3628
+ throw new Error("Remote image URLs are not allowed");
3629
+ }
3630
+ if (lowerSource.startsWith("file://")) {
3631
+ return nativeImage.createFromPath(fileURLToPath(normalizedSource));
3632
+ }
3633
+ return nativeImage.createFromPath(normalizedSource);
3634
+ }
3447
3635
  function registerFileHandlers() {
3448
3636
  ipcMain.handle(
3449
3637
  IPC_CHANNELS.FILE.GET_IMAGE_PATH,
@@ -3506,6 +3694,25 @@ function registerFileHandlers() {
3506
3694
  }
3507
3695
  }
3508
3696
  );
3697
+ ipcMain.handle(
3698
+ IPC_CHANNELS.FILE.COPY_IMAGE_TO_CLIPBOARD,
3699
+ async (_, imageSource) => {
3700
+ try {
3701
+ if (!imageSource) {
3702
+ return { success: false, error: "Image source is required" };
3703
+ }
3704
+ const image = await createImageFromSource(imageSource);
3705
+ if (image.isEmpty()) {
3706
+ return { success: false, error: "Image data is empty or invalid" };
3707
+ }
3708
+ clipboard.writeImage(image);
3709
+ return { success: true, data: { copied: true } };
3710
+ } catch (error) {
3711
+ console.error("[IPC] file:copyImageToClipboard error:", error);
3712
+ return { success: false, error: error.message || "Failed to copy image" };
3713
+ }
3714
+ }
3715
+ );
3509
3716
  ipcMain.handle(IPC_CHANNELS.FILE.SELECT_DIALOG, async (_, allowOffice) => {
3510
3717
  try {
3511
3718
  const pdfAndImageExtensions = ["pdf", "jpg", "jpeg", "png", "bmp", "gif"];
@@ -4373,6 +4580,88 @@ class CloudService {
4373
4580
  static instance;
4374
4581
  constructor() {
4375
4582
  }
4583
+ extractDownloadFileName(contentDisposition, fallback) {
4584
+ const rfc5987Name = this.parseRFC5987Filename(contentDisposition);
4585
+ if (rfc5987Name) {
4586
+ return this.sanitizeDownloadFileName(rfc5987Name, fallback);
4587
+ }
4588
+ const plainName = this.parsePlainFilename(contentDisposition);
4589
+ if (!plainName) {
4590
+ return this.sanitizeDownloadFileName(fallback, fallback);
4591
+ }
4592
+ const repairedName = this.tryRepairUtf8Mojibake(plainName);
4593
+ return this.sanitizeDownloadFileName(repairedName || plainName, fallback);
4594
+ }
4595
+ parseRFC5987Filename(contentDisposition) {
4596
+ const match = contentDisposition.match(/filename\*\s*=\s*([^;]+)/i);
4597
+ if (!match) return null;
4598
+ const rawValue = match[1]?.trim();
4599
+ if (!rawValue) return null;
4600
+ const unquoted = rawValue.replace(/^"(.*)"$/, "$1");
4601
+ const parts = unquoted.match(/^([^']*)'[^']*'(.*)$/);
4602
+ if (!parts) return null;
4603
+ const charset = (parts[1] || "utf-8").trim().toLowerCase();
4604
+ const encodedValue = parts[2] || "";
4605
+ try {
4606
+ if (charset === "utf-8" || charset === "utf8") {
4607
+ return decodeURIComponent(encodedValue);
4608
+ }
4609
+ const bytes = this.percentDecodeToBytes(encodedValue);
4610
+ if (charset === "iso-8859-1" || charset === "latin1") {
4611
+ return Buffer.from(bytes).toString("latin1");
4612
+ }
4613
+ return Buffer.from(bytes).toString("utf8");
4614
+ } catch {
4615
+ return null;
4616
+ }
4617
+ }
4618
+ parsePlainFilename(contentDisposition) {
4619
+ const match = contentDisposition.match(/filename\s*=\s*("(?:\\.|[^"])*"|[^;]+)/i);
4620
+ if (!match) return null;
4621
+ let value = match[1]?.trim();
4622
+ if (!value) return null;
4623
+ if (value.startsWith('"') && value.endsWith('"')) {
4624
+ value = value.slice(1, -1).replace(/\\"/g, '"');
4625
+ }
4626
+ return value;
4627
+ }
4628
+ percentDecodeToBytes(input) {
4629
+ const bytes = [];
4630
+ for (let i = 0; i < input.length; i++) {
4631
+ const ch = input[i];
4632
+ if (ch === "%" && i + 2 < input.length) {
4633
+ const hex = input.slice(i + 1, i + 3);
4634
+ const parsed = Number.parseInt(hex, 16);
4635
+ if (!Number.isNaN(parsed)) {
4636
+ bytes.push(parsed);
4637
+ i += 2;
4638
+ continue;
4639
+ }
4640
+ }
4641
+ bytes.push(input.charCodeAt(i));
4642
+ }
4643
+ return bytes;
4644
+ }
4645
+ tryRepairUtf8Mojibake(input) {
4646
+ const hasCjk = /[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/.test(input);
4647
+ if (hasCjk) return null;
4648
+ const latinSupplementCount = Array.from(input).filter((ch) => {
4649
+ const code = ch.charCodeAt(0);
4650
+ return code >= 192 && code <= 255;
4651
+ }).length;
4652
+ if (latinSupplementCount < 2) return null;
4653
+ const repaired = Buffer.from(input, "latin1").toString("utf8");
4654
+ if (!repaired) return null;
4655
+ const repairedHasCjk = /[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/.test(repaired);
4656
+ const roundTrip = Buffer.from(repaired, "utf8").toString("latin1") === input;
4657
+ if (repairedHasCjk && roundTrip) {
4658
+ return repaired;
4659
+ }
4660
+ return null;
4661
+ }
4662
+ sanitizeDownloadFileName(input, fallback) {
4663
+ return path.basename(input).replace(/[\u0000-\u001f<>:"|?*]/g, "_") || fallback;
4664
+ }
4376
4665
  normalizeCheckoutStatus(data) {
4377
4666
  if (!data || typeof data !== "object") {
4378
4667
  return null;
@@ -4600,11 +4889,17 @@ class CloudService {
4600
4889
  /**
4601
4890
  * Retry an entire task (creates a new task)
4602
4891
  */
4603
- async retryTask(id) {
4892
+ async retryTask(id, model) {
4604
4893
  try {
4605
- const res = await authManager.fetchWithAuth(`${API_BASE_URL}/api/v1/tasks/${encodeURIComponent(id)}/retry`, {
4606
- method: "POST"
4607
- });
4894
+ const hasModelOverride = typeof model === "string" && model.length > 0;
4895
+ const res = await authManager.fetchWithAuth(
4896
+ `${API_BASE_URL}/api/v1/tasks/${encodeURIComponent(id)}/retry`,
4897
+ hasModelOverride ? {
4898
+ method: "POST",
4899
+ headers: { "Content-Type": "application/json" },
4900
+ body: JSON.stringify({ model })
4901
+ } : { method: "POST" }
4902
+ );
4608
4903
  if (!res.ok) {
4609
4904
  const errorBody = await res.json().catch(() => null);
4610
4905
  return {
@@ -4628,11 +4923,16 @@ class CloudService {
4628
4923
  /**
4629
4924
  * Retry a single page
4630
4925
  */
4631
- async retryPage(taskId, pageNumber) {
4926
+ async retryPage(taskId, pageNumber, model) {
4632
4927
  try {
4928
+ const hasModelOverride = typeof model === "string" && model.length > 0;
4633
4929
  const res = await authManager.fetchWithAuth(
4634
4930
  `${API_BASE_URL}/api/v1/tasks/${encodeURIComponent(taskId)}/pages/${encodeURIComponent(String(pageNumber))}/retry`,
4635
- { method: "POST" }
4931
+ hasModelOverride ? {
4932
+ method: "POST",
4933
+ headers: { "Content-Type": "application/json" },
4934
+ body: JSON.stringify({ model })
4935
+ } : { method: "POST" }
4636
4936
  );
4637
4937
  if (!res.ok) {
4638
4938
  const errorBody = await res.json().catch(() => null);
@@ -4694,9 +4994,8 @@ class CloudService {
4694
4994
  };
4695
4995
  }
4696
4996
  const contentDisposition = res.headers.get("Content-Disposition") || "";
4697
- const match = contentDisposition.match(/filename="?([^";\n]+)"?/);
4698
- const rawName = match ? match[1] : `task-${id}.pdf`;
4699
- const fileName = path.basename(rawName).replace(/[\u0000-\u001f<>:"|?*]/g, "_") || `task-${id}.pdf`;
4997
+ const fallbackName = `task-${id}.pdf`;
4998
+ const fileName = this.extractDownloadFileName(contentDisposition, fallbackName);
4700
4999
  const buffer = await res.arrayBuffer();
4701
5000
  return { success: true, data: { buffer, fileName } };
4702
5001
  } catch (error) {
@@ -5314,9 +5613,10 @@ function registerCloudHandlers() {
5314
5613
  };
5315
5614
  }
5316
5615
  });
5317
- ipcMain.handle("cloud:retryTask", async (_, id) => {
5616
+ ipcMain.handle("cloud:retryTask", async (_, params) => {
5318
5617
  try {
5319
- return await cloudService.retryTask(id);
5618
+ const payload = typeof params === "string" ? { id: params } : params;
5619
+ return await cloudService.retryTask(payload.id, payload.model);
5320
5620
  } catch (error) {
5321
5621
  console.error("[IPC] cloud:retryTask error:", error);
5322
5622
  return {
@@ -5338,7 +5638,7 @@ function registerCloudHandlers() {
5338
5638
  });
5339
5639
  ipcMain.handle("cloud:retryPage", async (_, params) => {
5340
5640
  try {
5341
- return await cloudService.retryPage(params.taskId, params.pageNumber);
5641
+ return await cloudService.retryPage(params.taskId, params.pageNumber, params.model);
5342
5642
  } catch (error) {
5343
5643
  console.error("[IPC] cloud:retryPage error:", error);
5344
5644
  return {
@@ -25,6 +25,7 @@ electron.contextBridge.exposeInMainWorld("api", {
25
25
  getAll: (params) => electron.ipcRenderer.invoke("task:getAll", params),
26
26
  getById: (id) => electron.ipcRenderer.invoke("task:getById", id),
27
27
  update: (id, data) => electron.ipcRenderer.invoke("task:update", id, data),
28
+ retry: (params) => electron.ipcRenderer.invoke("task:retry", params),
28
29
  delete: (id) => electron.ipcRenderer.invoke("task:delete", id),
29
30
  hasRunningTasks: () => electron.ipcRenderer.invoke("task:hasRunningTasks")
30
31
  },
@@ -32,7 +33,7 @@ electron.contextBridge.exposeInMainWorld("api", {
32
33
  taskDetail: {
33
34
  getByPage: (taskId, page) => electron.ipcRenderer.invoke("taskDetail:getByPage", taskId, page),
34
35
  getAllByTask: (taskId) => electron.ipcRenderer.invoke("taskDetail:getAllByTask", taskId),
35
- retry: (pageId) => electron.ipcRenderer.invoke("taskDetail:retry", pageId),
36
+ retry: (params) => electron.ipcRenderer.invoke("taskDetail:retry", params),
36
37
  retryFailed: (taskId) => electron.ipcRenderer.invoke("taskDetail:retryFailed", taskId)
37
38
  },
38
39
  // ==================== File APIs ====================
@@ -41,7 +42,8 @@ electron.contextBridge.exposeInMainWorld("api", {
41
42
  upload: (taskId, filePath) => electron.ipcRenderer.invoke("file:upload", taskId, filePath),
42
43
  uploadFileContent: (taskId, fileName, fileBuffer) => electron.ipcRenderer.invoke("file:uploadFileContent", taskId, fileName, fileBuffer),
43
44
  getImagePath: (taskId, page) => electron.ipcRenderer.invoke("file:getImagePath", taskId, page),
44
- downloadMarkdown: (taskId) => electron.ipcRenderer.invoke("file:downloadMarkdown", taskId)
45
+ downloadMarkdown: (taskId) => electron.ipcRenderer.invoke("file:downloadMarkdown", taskId),
46
+ copyImageToClipboard: (imageSource) => electron.ipcRenderer.invoke("file:copyImageToClipboard", imageSource)
45
47
  },
46
48
  // ==================== Completion APIs ====================
47
49
  completion: {
@@ -62,7 +64,7 @@ electron.contextBridge.exposeInMainWorld("api", {
62
64
  getTaskById: (id) => electron.ipcRenderer.invoke("cloud:getTaskById", id),
63
65
  getTaskPages: (params) => electron.ipcRenderer.invoke("cloud:getTaskPages", params),
64
66
  cancelTask: (id) => electron.ipcRenderer.invoke("cloud:cancelTask", id),
65
- retryTask: (id) => electron.ipcRenderer.invoke("cloud:retryTask", id),
67
+ retryTask: (params) => electron.ipcRenderer.invoke("cloud:retryTask", params),
66
68
  deleteTask: (id) => electron.ipcRenderer.invoke("cloud:deleteTask", id),
67
69
  retryPage: (params) => electron.ipcRenderer.invoke("cloud:retryPage", params),
68
70
  getTaskResult: (id) => electron.ipcRenderer.invoke("cloud:getTaskResult", id),