markpdfdown 0.4.3 → 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.
@@ -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
  },
@@ -3123,6 +3142,123 @@ function registerTaskHandlers() {
3123
3142
  }
3124
3143
  }
3125
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
+ );
3126
3262
  ipcMain.handle(IPC_CHANNELS.TASK.DELETE, async (_, id) => {
3127
3263
  try {
3128
3264
  await fileLogic.deleteTaskFiles(id);
@@ -3233,11 +3369,19 @@ function registerTaskDetailHandlers() {
3233
3369
  );
3234
3370
  ipcMain.handle(
3235
3371
  IPC_CHANNELS.TASK_DETAIL.RETRY,
3236
- async (_, pageId) => {
3372
+ async (_, params) => {
3237
3373
  try {
3374
+ const payload = typeof params === "number" ? { pageId: params } : params;
3375
+ const pageId = payload?.pageId;
3238
3376
  if (!pageId) {
3239
3377
  return { success: false, error: "Page ID is required" };
3240
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
+ }
3241
3385
  const result = await prisma.$transaction(async (tx) => {
3242
3386
  const page = await tx.taskDetail.findUnique({
3243
3387
  where: { id: pageId }
@@ -3257,6 +3401,33 @@ function registerTaskDetailHandlers() {
3257
3401
  if (task.status === TaskStatus.CANCELLED) {
3258
3402
  throw new Error("Task is cancelled, cannot retry");
3259
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
+ }
3260
3431
  const updatedPage = await tx.taskDetail.update({
3261
3432
  where: { id: pageId },
3262
3433
  data: {
@@ -3269,7 +3440,9 @@ function registerTaskDetailHandlers() {
3269
3440
  input_tokens: 0,
3270
3441
  output_tokens: 0,
3271
3442
  conversion_time: 0,
3272
- content: ""
3443
+ content: "",
3444
+ provider: targetProvider,
3445
+ model: targetModel
3273
3446
  }
3274
3447
  });
3275
3448
  const decrementField = page.status === PageStatus.FAILED ? "failed_count" : "completed_count";
@@ -4716,11 +4889,17 @@ class CloudService {
4716
4889
  /**
4717
4890
  * Retry an entire task (creates a new task)
4718
4891
  */
4719
- async retryTask(id) {
4892
+ async retryTask(id, model) {
4720
4893
  try {
4721
- const res = await authManager.fetchWithAuth(`${API_BASE_URL}/api/v1/tasks/${encodeURIComponent(id)}/retry`, {
4722
- method: "POST"
4723
- });
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
+ );
4724
4903
  if (!res.ok) {
4725
4904
  const errorBody = await res.json().catch(() => null);
4726
4905
  return {
@@ -4744,11 +4923,16 @@ class CloudService {
4744
4923
  /**
4745
4924
  * Retry a single page
4746
4925
  */
4747
- async retryPage(taskId, pageNumber) {
4926
+ async retryPage(taskId, pageNumber, model) {
4748
4927
  try {
4928
+ const hasModelOverride = typeof model === "string" && model.length > 0;
4749
4929
  const res = await authManager.fetchWithAuth(
4750
4930
  `${API_BASE_URL}/api/v1/tasks/${encodeURIComponent(taskId)}/pages/${encodeURIComponent(String(pageNumber))}/retry`,
4751
- { method: "POST" }
4931
+ hasModelOverride ? {
4932
+ method: "POST",
4933
+ headers: { "Content-Type": "application/json" },
4934
+ body: JSON.stringify({ model })
4935
+ } : { method: "POST" }
4752
4936
  );
4753
4937
  if (!res.ok) {
4754
4938
  const errorBody = await res.json().catch(() => null);
@@ -5429,9 +5613,10 @@ function registerCloudHandlers() {
5429
5613
  };
5430
5614
  }
5431
5615
  });
5432
- ipcMain.handle("cloud:retryTask", async (_, id) => {
5616
+ ipcMain.handle("cloud:retryTask", async (_, params) => {
5433
5617
  try {
5434
- return await cloudService.retryTask(id);
5618
+ const payload = typeof params === "string" ? { id: params } : params;
5619
+ return await cloudService.retryTask(payload.id, payload.model);
5435
5620
  } catch (error) {
5436
5621
  console.error("[IPC] cloud:retryTask error:", error);
5437
5622
  return {
@@ -5453,7 +5638,7 @@ function registerCloudHandlers() {
5453
5638
  });
5454
5639
  ipcMain.handle("cloud:retryPage", async (_, params) => {
5455
5640
  try {
5456
- return await cloudService.retryPage(params.taskId, params.pageNumber);
5641
+ return await cloudService.retryPage(params.taskId, params.pageNumber, params.model);
5457
5642
  } catch (error) {
5458
5643
  console.error("[IPC] cloud:retryPage error:", error);
5459
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 ====================
@@ -63,7 +64,7 @@ electron.contextBridge.exposeInMainWorld("api", {
63
64
  getTaskById: (id) => electron.ipcRenderer.invoke("cloud:getTaskById", id),
64
65
  getTaskPages: (params) => electron.ipcRenderer.invoke("cloud:getTaskPages", params),
65
66
  cancelTask: (id) => electron.ipcRenderer.invoke("cloud:cancelTask", id),
66
- retryTask: (id) => electron.ipcRenderer.invoke("cloud:retryTask", id),
67
+ retryTask: (params) => electron.ipcRenderer.invoke("cloud:retryTask", params),
67
68
  deleteTask: (id) => electron.ipcRenderer.invoke("cloud:deleteTask", id),
68
69
  retryPage: (params) => electron.ipcRenderer.invoke("cloud:retryPage", params),
69
70
  getTaskResult: (id) => electron.ipcRenderer.invoke("cloud:getTaskResult", id),
@@ -70612,7 +70612,7 @@ const AppLayout = () => {
70612
70612
  };
70613
70613
  const { Text: Text$5 } = Typography;
70614
70614
  const CLOUD_PROVIDER_ID = -1;
70615
- const CLOUD_MODEL_TIERS = [
70615
+ const CLOUD_MODEL_TIERS$2 = [
70616
70616
  { id: "lite", name: "Fit Lite", creditsPerPage: 10 },
70617
70617
  { id: "pro", name: "Fit Pro", creditsPerPage: 20 },
70618
70618
  { id: "ultra", name: "Fit Ultra", creditsPerPage: 60 }
@@ -70672,7 +70672,7 @@ const UploadPanel = () => {
70672
70672
  const cloudGroup = {
70673
70673
  provider: CLOUD_PROVIDER_ID,
70674
70674
  providerName: t2("cloud.provider_name"),
70675
- models: CLOUD_MODEL_TIERS.map((tier) => ({
70675
+ models: CLOUD_MODEL_TIERS$2.map((tier) => ({
70676
70676
  id: tier.id,
70677
70677
  name: `${tier.name} (${t2(`cloud.tier_${tier.id}`)})`,
70678
70678
  provider: CLOUD_PROVIDER_ID
@@ -71128,6 +71128,7 @@ function mapCloudTasksToTasks(cloudTasks) {
71128
71128
  return cloudTasks.map(mapCloudTaskToTask);
71129
71129
  }
71130
71130
  const { Text: Text$4 } = Typography;
71131
+ const CLOUD_MODEL_TIERS$1 = ["lite", "pro", "ultra"];
71131
71132
  const List = () => {
71132
71133
  const { message: message2, modal } = App$1.useApp();
71133
71134
  const { t: t2 } = useTranslation("list");
@@ -71146,6 +71147,45 @@ const List = () => {
71146
71147
  const paginationRef = reactExports.useRef(pagination);
71147
71148
  paginationRef.current = pagination;
71148
71149
  const MAX_FETCH_ITEMS = 100;
71150
+ const buildLocalModelValue = (modelId, providerId) => `${modelId}@${providerId}`;
71151
+ const parseLocalModelValue = (value) => {
71152
+ const separatorIndex = value.lastIndexOf("@");
71153
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
71154
+ return null;
71155
+ }
71156
+ const modelId = value.slice(0, separatorIndex);
71157
+ const providerId = Number(value.slice(separatorIndex + 1));
71158
+ if (!modelId || !Number.isInteger(providerId)) {
71159
+ return null;
71160
+ }
71161
+ return {
71162
+ modelId,
71163
+ providerId
71164
+ };
71165
+ };
71166
+ const loadLocalModelOptions = reactExports.useCallback(async () => {
71167
+ const result = await window.api.model.getAll();
71168
+ if (!result.success || !result.data) {
71169
+ throw new Error(result.error || t2("retry.load_models_failed"));
71170
+ }
71171
+ const modelGroups = result.data;
71172
+ return modelGroups.flatMap(
71173
+ (group) => group.models.map((model) => ({
71174
+ value: buildLocalModelValue(model.id, group.provider),
71175
+ label: `${model.name} | ${group.providerName}`
71176
+ }))
71177
+ );
71178
+ }, [t2]);
71179
+ const parseCloudModelTier = (record) => {
71180
+ const explicitTier = record?.model_tier;
71181
+ if (explicitTier && CLOUD_MODEL_TIERS$1.includes(explicitTier)) {
71182
+ return explicitTier;
71183
+ }
71184
+ const modelName = (record.model_name || "").toLowerCase();
71185
+ if (modelName.includes("ultra")) return "ultra";
71186
+ if (modelName.includes("pro")) return "pro";
71187
+ return "lite";
71188
+ };
71149
71189
  const fetchTasks = reactExports.useCallback(async (page = 1, pageSize = 10) => {
71150
71190
  setLoading(true);
71151
71191
  try {
@@ -71408,8 +71448,62 @@ const List = () => {
71408
71448
  }
71409
71449
  });
71410
71450
  };
71411
- const handleRetryTask = (id) => {
71412
- handleUpdateTaskStatus(id, 1, t2("actions.retry"));
71451
+ const handleRetryTask = async (task) => {
71452
+ if (!task.id) return;
71453
+ try {
71454
+ const modelOptions = await loadLocalModelOptions();
71455
+ if (modelOptions.length === 0) {
71456
+ message2.error(t2("retry.no_models_available"));
71457
+ return;
71458
+ }
71459
+ const defaultModelValue = task.model && task.provider !== void 0 && task.provider >= 0 ? buildLocalModelValue(task.model, task.provider) : modelOptions[0].value;
71460
+ let selectedModelValue = modelOptions.some((opt) => opt.value === defaultModelValue) ? defaultModelValue : modelOptions[0].value;
71461
+ modal.confirm({
71462
+ title: t2("retry.confirm_with_model"),
71463
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
71464
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("retry.select_model") }),
71465
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
71466
+ Select,
71467
+ {
71468
+ style: { width: "100%" },
71469
+ options: modelOptions,
71470
+ defaultValue: selectedModelValue,
71471
+ onChange: (value) => {
71472
+ selectedModelValue = value;
71473
+ }
71474
+ }
71475
+ )
71476
+ ] }),
71477
+ okText: t2("confirmations.ok"),
71478
+ cancelText: t2("confirmations.cancel"),
71479
+ onOk: async () => {
71480
+ const parsedModel = parseLocalModelValue(selectedModelValue);
71481
+ if (!parsedModel) {
71482
+ message2.error(t2("messages.action_failed", { action: t2("actions.retry") }));
71483
+ return;
71484
+ }
71485
+ const { modelId, providerId } = parsedModel;
71486
+ try {
71487
+ const result = await window.api.task.retry({
71488
+ taskId: task.id,
71489
+ providerId,
71490
+ modelId
71491
+ });
71492
+ if (result.success) {
71493
+ message2.success(t2("messages.action_success", { action: t2("actions.retry") }));
71494
+ fetchTasks(pagination.current, pagination.pageSize);
71495
+ } else {
71496
+ message2.error(result.error || t2("messages.action_failed", { action: t2("actions.retry") }));
71497
+ }
71498
+ } catch (error) {
71499
+ console.error("Failed to retry local task:", error);
71500
+ message2.error(t2("messages.action_failed", { action: t2("actions.retry") }));
71501
+ }
71502
+ }
71503
+ });
71504
+ } catch (error) {
71505
+ message2.error(error instanceof Error ? error.message : t2("retry.load_models_failed"));
71506
+ }
71413
71507
  };
71414
71508
  const handleCancelTask = (id) => {
71415
71509
  handleUpdateTaskStatus(id, 7, t2("actions.cancel"));
@@ -71436,16 +71530,34 @@ const List = () => {
71436
71530
  }
71437
71531
  });
71438
71532
  };
71439
- const handleCloudRetryTask = async (id) => {
71533
+ const handleCloudRetryTask = async (task) => {
71440
71534
  if (!cloudContext) return;
71535
+ if (!task.id) return;
71536
+ let selectedModel = parseCloudModelTier(task);
71441
71537
  modal.confirm({
71442
- title: t2("confirmations.cancel_title", { action: t2("actions.retry") }),
71443
- content: t2("confirmations.cancel_content", { action: t2("actions.retry") }),
71538
+ title: t2("retry.confirm_cloud_with_model"),
71539
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
71540
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("retry.select_model") }),
71541
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
71542
+ Select,
71543
+ {
71544
+ style: { width: "100%" },
71545
+ options: CLOUD_MODEL_TIERS$1.map((tier) => ({
71546
+ value: tier,
71547
+ label: t2(`retry.model.${tier}`)
71548
+ })),
71549
+ defaultValue: selectedModel,
71550
+ onChange: (value) => {
71551
+ selectedModel = value;
71552
+ }
71553
+ }
71554
+ )
71555
+ ] }),
71444
71556
  okText: t2("confirmations.ok"),
71445
71557
  cancelText: t2("confirmations.cancel"),
71446
71558
  onOk: async () => {
71447
71559
  try {
71448
- const result = await cloudContext.retryTask(id);
71560
+ const result = await cloudContext.retryTask(task.id, selectedModel);
71449
71561
  if (result.success) {
71450
71562
  message2.success(t2("messages.action_success", { action: t2("actions.retry") }));
71451
71563
  fetchTasks(pagination.current, pagination.pageSize);
@@ -71645,7 +71757,7 @@ const List = () => {
71645
71757
  {
71646
71758
  type: "warning",
71647
71759
  style: { cursor: "pointer" },
71648
- onClick: () => record.id && (isCloud ? handleCloudRetryTask(record.id) : handleRetryTask(record.id)),
71760
+ onClick: () => record.id && (isCloud ? handleCloudRetryTask(record) : handleRetryTask(record)),
71649
71761
  children: t2("actions.retry")
71650
71762
  }
71651
71763
  );
@@ -125336,6 +125448,35 @@ const Preview = () => {
125336
125448
  const [imageError, setImageError] = reactExports.useState(false);
125337
125449
  const [retrying, setRetrying] = reactExports.useState(false);
125338
125450
  const [retryingFailed, setRetryingFailed] = reactExports.useState(false);
125451
+ const buildModelValue = (modelId, providerId) => `${modelId}@${providerId}`;
125452
+ const parseModelValue = (value) => {
125453
+ const separatorIndex = value.lastIndexOf("@");
125454
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
125455
+ return null;
125456
+ }
125457
+ const modelId = value.slice(0, separatorIndex);
125458
+ const providerId = Number(value.slice(separatorIndex + 1));
125459
+ if (!modelId || !Number.isInteger(providerId)) {
125460
+ return null;
125461
+ }
125462
+ return {
125463
+ modelId,
125464
+ providerId
125465
+ };
125466
+ };
125467
+ const loadLocalModelOptions = reactExports.useCallback(async () => {
125468
+ const result = await window.api.model.getAll();
125469
+ if (!result.success || !result.data) {
125470
+ throw new Error(result.error || t2("preview.load_models_failed"));
125471
+ }
125472
+ const modelGroups = result.data;
125473
+ return modelGroups.flatMap(
125474
+ (group) => group.models.map((model) => ({
125475
+ value: buildModelValue(model.id, group.provider),
125476
+ label: `${model.name} | ${group.providerName}`
125477
+ }))
125478
+ );
125479
+ }, [t2]);
125339
125480
  const fetchTask = reactExports.useCallback(async () => {
125340
125481
  if (!id) return;
125341
125482
  try {
@@ -125489,26 +125630,63 @@ const Preview = () => {
125489
125630
  });
125490
125631
  };
125491
125632
  const handleRetryTask = async () => {
125492
- if (!id) return;
125493
- modal.confirm({
125494
- title: t2("preview.confirm_retry"),
125495
- content: t2("preview.confirm_retry_content"),
125496
- okText: tCommon("common.confirm"),
125497
- cancelText: tCommon("common.cancel"),
125498
- onOk: async () => {
125499
- try {
125500
- const result = await window.api.task.update(id, { status: 1 });
125501
- if (result.success) {
125502
- message2.success(t2("preview.retry_success"));
125503
- } else {
125504
- message2.error(result.error || t2("preview.retry_failed"));
125633
+ if (!id || !task) return;
125634
+ try {
125635
+ const modelOptions = await loadLocalModelOptions();
125636
+ if (modelOptions.length === 0) {
125637
+ message2.error(t2("preview.no_models_available"));
125638
+ return;
125639
+ }
125640
+ const defaultModelValue = buildModelValue(task.model || "", task.provider || 0);
125641
+ let selectedModelValue = modelOptions.some((opt) => opt.value === defaultModelValue) ? defaultModelValue : modelOptions[0].value;
125642
+ modal.confirm({
125643
+ title: t2("preview.confirm_retry_with_model"),
125644
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
125645
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("preview.select_retry_model") }),
125646
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
125647
+ Select,
125648
+ {
125649
+ style: { width: "100%" },
125650
+ options: modelOptions,
125651
+ defaultValue: selectedModelValue,
125652
+ onChange: (value) => {
125653
+ selectedModelValue = value;
125654
+ }
125655
+ }
125656
+ )
125657
+ ] }),
125658
+ okText: tCommon("common.confirm"),
125659
+ cancelText: tCommon("common.cancel"),
125660
+ onOk: async () => {
125661
+ try {
125662
+ const shouldOverride = selectedModelValue !== defaultModelValue;
125663
+ let overridePayload = null;
125664
+ if (shouldOverride) {
125665
+ const parsedModel = parseModelValue(selectedModelValue);
125666
+ if (!parsedModel) {
125667
+ message2.error(t2("preview.retry_failed"));
125668
+ return;
125669
+ }
125670
+ overridePayload = parsedModel;
125671
+ }
125672
+ const result = await window.api.task.retry({
125673
+ taskId: id,
125674
+ ...overridePayload || {}
125675
+ });
125676
+ if (result.success) {
125677
+ message2.success(t2("preview.retry_success"));
125678
+ } else {
125679
+ message2.error(result.error || t2("preview.retry_failed"));
125680
+ }
125681
+ } catch (error) {
125682
+ console.error("重试失败:", error);
125683
+ message2.error(t2("preview.retry_failed"));
125505
125684
  }
125506
- } catch (error) {
125507
- console.error("重试失败:", error);
125508
- message2.error(t2("preview.retry_failed"));
125509
125685
  }
125510
- }
125511
- });
125686
+ });
125687
+ } catch (error) {
125688
+ message2.error(error instanceof Error ? error.message : t2("preview.load_models_failed"));
125689
+ }
125512
125690
  };
125513
125691
  const handleRetryFailed = async () => {
125514
125692
  if (!id) return;
@@ -125540,20 +125718,64 @@ const Preview = () => {
125540
125718
  };
125541
125719
  const handleRetryPage = async () => {
125542
125720
  if (!taskDetail?.id) return;
125543
- setRetrying(true);
125544
125721
  try {
125545
- const result = await window.api.taskDetail.retry(taskDetail.id);
125546
- if (result.success) {
125547
- message2.success(t2("preview.page_retry_success"));
125548
- fetchPageDetail(currentPage);
125549
- } else {
125550
- message2.error(result.error || t2("preview.page_retry_failed"));
125722
+ const modelOptions = await loadLocalModelOptions();
125723
+ if (modelOptions.length === 0) {
125724
+ message2.error(t2("preview.no_models_available"));
125725
+ return;
125551
125726
  }
125727
+ const defaultModelValue = buildModelValue(taskDetail.model, taskDetail.provider);
125728
+ let selectedModelValue = modelOptions.some((opt) => opt.value === defaultModelValue) ? defaultModelValue : modelOptions[0].value;
125729
+ modal.confirm({
125730
+ title: t2("preview.confirm_page_retry_with_model"),
125731
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
125732
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("preview.select_retry_model") }),
125733
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
125734
+ Select,
125735
+ {
125736
+ style: { width: "100%" },
125737
+ options: modelOptions,
125738
+ defaultValue: selectedModelValue,
125739
+ onChange: (value) => {
125740
+ selectedModelValue = value;
125741
+ }
125742
+ }
125743
+ )
125744
+ ] }),
125745
+ okText: tCommon("common.confirm"),
125746
+ cancelText: tCommon("common.cancel"),
125747
+ onOk: async () => {
125748
+ setRetrying(true);
125749
+ try {
125750
+ const shouldOverride = selectedModelValue !== defaultModelValue;
125751
+ let overridePayload = null;
125752
+ if (shouldOverride) {
125753
+ const parsedModel = parseModelValue(selectedModelValue);
125754
+ if (!parsedModel) {
125755
+ message2.error(t2("preview.page_retry_failed"));
125756
+ return;
125757
+ }
125758
+ overridePayload = parsedModel;
125759
+ }
125760
+ const result = await window.api.taskDetail.retry(
125761
+ overridePayload ? { pageId: taskDetail.id, ...overridePayload } : { pageId: taskDetail.id }
125762
+ );
125763
+ if (result.success) {
125764
+ message2.success(t2("preview.page_retry_success"));
125765
+ fetchPageDetail(currentPage);
125766
+ } else {
125767
+ message2.error(result.error || t2("preview.page_retry_failed"));
125768
+ }
125769
+ } catch (error) {
125770
+ console.error("Failed to retry page:", error);
125771
+ message2.error(t2("preview.page_retry_failed"));
125772
+ } finally {
125773
+ setRetrying(false);
125774
+ }
125775
+ }
125776
+ });
125552
125777
  } catch (error) {
125553
- console.error("Failed to retry page:", error);
125554
- message2.error(t2("preview.page_retry_failed"));
125555
- } finally {
125556
- setRetrying(false);
125778
+ message2.error(error instanceof Error ? error.message : t2("preview.load_models_failed"));
125557
125779
  }
125558
125780
  };
125559
125781
  const handleCopyMarkdown = async () => {
@@ -125703,7 +125925,7 @@ const Preview = () => {
125703
125925
  disabled: retryingFailed
125704
125926
  });
125705
125927
  }
125706
- if (status2 === 0) {
125928
+ if (status2 === 0 || status2 === 6) {
125707
125929
  menuItems.push({
125708
125930
  key: "retry_all",
125709
125931
  icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$M, {}),
@@ -125907,6 +126129,7 @@ const Preview = () => {
125907
126129
  };
125908
126130
  const { Text } = Typography;
125909
126131
  const dedupeAndSortPages = (pageItems) => Array.from(new Map(pageItems.map((page) => [page.page, page])).values()).sort((a2, b) => a2.page - b.page);
126132
+ const CLOUD_MODEL_TIERS = ["lite", "pro", "ultra"];
125910
126133
  const CloudPreview = () => {
125911
126134
  const { id } = useParams();
125912
126135
  const navigate = useNavigate();
@@ -126200,13 +126423,32 @@ const CloudPreview = () => {
126200
126423
  };
126201
126424
  const handleRetryTask = async () => {
126202
126425
  if (!id || !cloudContext) return;
126426
+ const options = CLOUD_MODEL_TIERS.map((tier) => ({
126427
+ value: tier,
126428
+ label: t2(`retry_model.${tier}`)
126429
+ }));
126430
+ const taskTier = task?.model_tier || "lite";
126431
+ let selectedModel = CLOUD_MODEL_TIERS.includes(taskTier) ? taskTier : "lite";
126203
126432
  modal.confirm({
126204
- title: t2("confirm_retry"),
126205
- content: t2("confirm_retry_content"),
126433
+ title: t2("confirm_retry_with_model"),
126434
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
126435
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("select_retry_model") }),
126436
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
126437
+ Select,
126438
+ {
126439
+ style: { width: "100%" },
126440
+ options,
126441
+ defaultValue: selectedModel,
126442
+ onChange: (value) => {
126443
+ selectedModel = value;
126444
+ }
126445
+ }
126446
+ )
126447
+ ] }),
126206
126448
  okText: tCommon("common.confirm"),
126207
126449
  cancelText: tCommon("common.cancel"),
126208
126450
  onOk: async () => {
126209
- const result = await cloudContext.retryTask(id);
126451
+ const result = await cloudContext.retryTask(id, selectedModel);
126210
126452
  if (result.success && result.data) {
126211
126453
  message2.success(t2("retry_success"));
126212
126454
  navigate(`/list/cloud-preview/${result.data.task_id}`);
@@ -126218,28 +126460,55 @@ const CloudPreview = () => {
126218
126460
  };
126219
126461
  const handleRetryPage = async () => {
126220
126462
  if (!id || !cloudContext || !currentPageData) return;
126221
- setRetrying(true);
126222
- try {
126223
- const result = await cloudContext.retryPage(id, currentPage);
126224
- if (result.success) {
126225
- message2.success(t2("page_retry_success"));
126226
- setPages((prev2) => {
126227
- const idx = prev2.findIndex((p2) => p2.page === currentPage);
126228
- if (idx >= 0) {
126229
- const updated = [...prev2];
126230
- updated[idx] = { ...updated[idx], status: 1 };
126231
- return updated;
126463
+ const options = CLOUD_MODEL_TIERS.map((tier) => ({
126464
+ value: tier,
126465
+ label: t2(`retry_model.${tier}`)
126466
+ }));
126467
+ const taskTier = task?.model_tier || "lite";
126468
+ let selectedModel = CLOUD_MODEL_TIERS.includes(taskTier) ? taskTier : "lite";
126469
+ modal.confirm({
126470
+ title: t2("confirm_page_retry_with_model"),
126471
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
126472
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("select_retry_model") }),
126473
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
126474
+ Select,
126475
+ {
126476
+ style: { width: "100%" },
126477
+ options,
126478
+ defaultValue: selectedModel,
126479
+ onChange: (value) => {
126480
+ selectedModel = value;
126481
+ }
126232
126482
  }
126233
- return prev2;
126234
- });
126235
- } else {
126236
- message2.error(result.error || t2("page_retry_failed"));
126483
+ )
126484
+ ] }),
126485
+ okText: tCommon("common.confirm"),
126486
+ cancelText: tCommon("common.cancel"),
126487
+ onOk: async () => {
126488
+ setRetrying(true);
126489
+ try {
126490
+ const result = await cloudContext.retryPage(id, currentPage, selectedModel);
126491
+ if (result.success) {
126492
+ message2.success(t2("page_retry_success"));
126493
+ setPages((prev2) => {
126494
+ const idx = prev2.findIndex((p2) => p2.page === currentPage);
126495
+ if (idx >= 0) {
126496
+ const updated = [...prev2];
126497
+ updated[idx] = { ...updated[idx], status: 1 };
126498
+ return updated;
126499
+ }
126500
+ return prev2;
126501
+ });
126502
+ } else {
126503
+ message2.error(result.error || t2("page_retry_failed"));
126504
+ }
126505
+ } catch {
126506
+ message2.error(t2("page_retry_failed"));
126507
+ } finally {
126508
+ setRetrying(false);
126509
+ }
126237
126510
  }
126238
- } catch {
126239
- message2.error(t2("page_retry_failed"));
126240
- } finally {
126241
- setRetrying(false);
126242
- }
126511
+ });
126243
126512
  };
126244
126513
  const handleRetryFailed = async () => {
126245
126514
  if (!id || !cloudContext) return;
@@ -126464,7 +126733,7 @@ const CloudPreview = () => {
126464
126733
  disabled: retryingFailed
126465
126734
  });
126466
126735
  }
126467
- if (status2 === 0) {
126736
+ if (status2 === 0 || status2 === 6) {
126468
126737
  menuItems.push({
126469
126738
  key: "retry_all",
126470
126739
  icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$M, {}),
@@ -128635,6 +128904,7 @@ const status$a = { "pending": "Pending", "initializing": "Initializing", "proces
128635
128904
  const actions$g = { "view": "View", "cancel": "Cancel", "retry": "Retry", "delete": "Delete" };
128636
128905
  const confirmations$b = { "delete_title": "Confirm Delete", "delete_content": "Are you sure you want to delete this task? This action cannot be undone.", "cancel_title": "Confirm {{action}}", "cancel_content": "Are you sure you want to {{action}} this task?", "ok": "Confirm", "cancel": "Cancel" };
128637
128906
  const messages$h = { "fetch_failed": "Failed to fetch task list", "delete_success": "Deleted successfully", "delete_failed": "Failed to delete task", "action_success": "{{action}} successful", "action_failed": "{{action}} failed" };
128907
+ const retry$1 = { "confirm_with_model": "Retry Task (Choose Model)", "confirm_cloud_with_model": "Retry Cloud Task (Choose Model)", "select_model": "Select retry model", "load_models_failed": "Failed to load model list", "no_models_available": "No available models. Please configure models in settings first.", "model": { "lite": "Fit Lite", "pro": "Fit Pro", "ultra": "Fit Ultra" } };
128638
128908
  const task_type$5 = { "cloud": "Cloud Task", "local": "Local Task" };
128639
128909
  const enList = {
128640
128910
  columns: columns$5,
@@ -128642,6 +128912,7 @@ const enList = {
128642
128912
  actions: actions$g,
128643
128913
  confirmations: confirmations$b,
128644
128914
  messages: messages$h,
128915
+ retry: retry$1,
128645
128916
  task_type: task_type$5
128646
128917
  };
128647
128918
  const dragger$5 = { "text": "Drag files here or click button below to select files", "hint": "Supports single or batch upload of PDF/JPG/PNG files", "hint_local": "Supports PDF and image files. Sign in to enjoy free conversion and support for more file types", "hint_logged_in_non_cloud": "Currently only PDF or image files are supported. Select a Markdown.Fit cloud model to enable Office files", "hint_cloud_logged_in": "Supports PDF, image, and Office files (Word/Excel/PPT)", "login_hint": "Sign in to enjoy free conversion and support for more file types", "button": "Select Files" };
@@ -128673,7 +128944,7 @@ const enProvider = {
128673
128944
  const tabs$5 = { "account": "Account", "model_service": "Model Service", "about": "About Us" };
128674
128945
  const about$5 = { "subtitle": "A high-quality PDF to Markdown converter powered by large vision models", "buttons": { "website": "Official Website", "license": "License", "feedback": "Feedback", "contact": "Contact Email" } };
128675
128946
  const update$5 = { "check_for_updates": "Check for Updates", "checking": "Checking for updates...", "available": "Update available", "downloading": "Downloading update...", "ready": "Update ready to install", "restart_to_update": "Restart to Update", "up_to_date": "You are up to date", "error": "Update check failed", "retry": "Retry" };
128676
- const preview$5 = { "back": "Back", "download": "Download .md", "delete": "Delete", "cancel": "Cancel", "retry": "Retry", "retry_failed": "Retry Failed", "retry_all": "Retry All", "more_actions": "More Actions", "regenerate": "Regenerate", "regenerate_tooltip": "Regenerate current page", "copy_markdown": "Copy Markdown", "copy_markdown_tooltip": "Copy current page markdown", "copy_markdown_success": "Markdown copied", "copy_markdown_failed": "Failed to copy markdown", "copy_image": "Copy Image", "copy_image_tooltip": "Copy current page image", "copy_image_success": "Image copied", "copy_image_failed": "Failed to copy image", "confirm_delete": "Confirm Delete", "confirm_delete_content": "Are you sure you want to delete this task? This action cannot be undone.", "delete_success": "Deleted successfully", "delete_failed": "Delete failed", "confirm_cancel": "Confirm Cancel", "confirm_cancel_content": "Are you sure you want to cancel this task?", "cancel_success": "Cancelled successfully", "cancel_failed": "Cancel failed", "confirm_retry": "Confirm Retry", "confirm_retry_content": "Are you sure you want to retry this task?", "confirm_retry_failed": "Confirm Retry Failed Pages", "confirm_retry_failed_content": "Are you sure you want to retry all failed pages?", "retry_success": "Task has been added to retry queue", "retry_failed_success": "{{count}} failed pages have been added to retry queue", "page_retry_success": "Page has been added to retry queue", "page_retry_failed": "Retry failed", "fetch_task_failed": "Failed to fetch task info", "fetch_page_failed": "Failed to fetch page details", "task_deleted": "Task has been deleted", "task_not_started": "Task has not started processing, cannot preview", "download_success": "Download successful", "download_failed": "Download failed", "image_load_failed": "Image failed to load or does not exist", "status": { "failed": "Conversion failed", "pending": "Pending", "processing": "Converting", "completed": "Completed", "retrying": "Retrying" } };
128947
+ const preview$5 = { "back": "Back", "download": "Download .md", "delete": "Delete", "cancel": "Cancel", "retry": "Retry", "retry_failed": "Retry Failed", "retry_all": "Retry All", "more_actions": "More Actions", "regenerate": "Regenerate", "regenerate_tooltip": "Regenerate current page", "copy_markdown": "Copy Markdown", "copy_markdown_tooltip": "Copy current page markdown", "copy_markdown_success": "Markdown copied", "copy_markdown_failed": "Failed to copy markdown", "copy_image": "Copy Image", "copy_image_tooltip": "Copy current page image", "copy_image_success": "Image copied", "copy_image_failed": "Failed to copy image", "confirm_delete": "Confirm Delete", "confirm_delete_content": "Are you sure you want to delete this task? This action cannot be undone.", "delete_success": "Deleted successfully", "delete_failed": "Delete failed", "confirm_cancel": "Confirm Cancel", "confirm_cancel_content": "Are you sure you want to cancel this task?", "cancel_success": "Cancelled successfully", "cancel_failed": "Cancel failed", "confirm_retry": "Confirm Retry", "confirm_retry_content": "Are you sure you want to retry this task?", "confirm_retry_with_model": "Retry Task (Choose Model)", "confirm_page_retry_with_model": "Retry Current Page (Choose Model)", "select_retry_model": "Select retry model", "confirm_retry_failed": "Confirm Retry Failed Pages", "confirm_retry_failed_content": "Are you sure you want to retry all failed pages?", "retry_success": "Task has been added to retry queue", "retry_failed_success": "{{count}} failed pages have been added to retry queue", "page_retry_success": "Page has been added to retry queue", "page_retry_failed": "Retry failed", "load_models_failed": "Failed to load model list", "no_models_available": "No available models. Please configure models in settings first.", "fetch_task_failed": "Failed to fetch task info", "fetch_page_failed": "Failed to fetch page details", "task_deleted": "Task has been deleted", "task_not_started": "Task has not started processing, cannot preview", "download_success": "Download successful", "download_failed": "Download failed", "image_load_failed": "Image failed to load or does not exist", "status": { "failed": "Conversion failed", "pending": "Pending", "processing": "Converting", "completed": "Completed", "retrying": "Retrying" } };
128677
128948
  const enSettings = {
128678
128949
  tabs: tabs$5,
128679
128950
  about: about$5,
@@ -128716,6 +128987,9 @@ const cancel_failed$5 = "Failed to cancel task";
128716
128987
  const cancel_task$5 = "Cancel";
128717
128988
  const confirm_retry$5 = "Retry Task";
128718
128989
  const confirm_retry_content$5 = "This will create a new task. Are you sure?";
128990
+ const confirm_retry_with_model$1 = "Retry Task (Choose Model)";
128991
+ const confirm_page_retry_with_model$1 = "Retry Current Page (Choose Model)";
128992
+ const select_retry_model$1 = "Select retry model";
128719
128993
  const retry_success$5 = "Retry started";
128720
128994
  const retry_failed$5 = "Retry failed";
128721
128995
  const retry_all$5 = "Retry All";
@@ -128743,6 +129017,7 @@ const copy_image$5 = "Copy Image";
128743
129017
  const copy_image_tooltip$5 = "Copy current page image";
128744
129018
  const copy_image_success$5 = "Image copied";
128745
129019
  const copy_image_failed$5 = "Failed to copy image";
129020
+ const retry_model$1 = { "lite": "Fit Lite (~10 credits/page)", "pro": "Fit Pro (~20 credits/page)", "ultra": "Fit Ultra (~60 credits/page)" };
128746
129021
  const page_status$5 = { "pending": "Pending", "processing": "Processing", "completed": "Completed", "failed": "Failed" };
128747
129022
  const enCloudPreview = {
128748
129023
  back: back$5,
@@ -128759,6 +129034,9 @@ const enCloudPreview = {
128759
129034
  cancel_task: cancel_task$5,
128760
129035
  confirm_retry: confirm_retry$5,
128761
129036
  confirm_retry_content: confirm_retry_content$5,
129037
+ confirm_retry_with_model: confirm_retry_with_model$1,
129038
+ confirm_page_retry_with_model: confirm_page_retry_with_model$1,
129039
+ select_retry_model: select_retry_model$1,
128762
129040
  retry_success: retry_success$5,
128763
129041
  retry_failed: retry_failed$5,
128764
129042
  retry_all: retry_all$5,
@@ -128786,6 +129064,7 @@ const enCloudPreview = {
128786
129064
  copy_image_tooltip: copy_image_tooltip$5,
128787
129065
  copy_image_success: copy_image_success$5,
128788
129066
  copy_image_failed: copy_image_failed$5,
129067
+ retry_model: retry_model$1,
128789
129068
  page_status: page_status$5
128790
129069
  };
128791
129070
  const navigation$4 = { "home": "主页", "list": "列表", "settings": "设置" };
@@ -128813,6 +129092,7 @@ const status$8 = { "pending": "待处理", "initializing": "初始化", "process
128813
129092
  const actions$d = { "view": "查看", "cancel": "取消", "retry": "重试", "delete": "删除" };
128814
129093
  const confirmations$9 = { "delete_title": "确认删除", "delete_content": "确定要删除这个任务吗?此操作不可逆。", "cancel_title": "确认{{action}}", "cancel_content": "确定要{{action}}这个任务吗?", "ok": "确定", "cancel": "取消" };
128815
129094
  const messages$e = { "fetch_failed": "获取任务列表失败", "delete_success": "删除成功", "delete_failed": "删除任务失败", "action_success": "{{action}}成功", "action_failed": "{{action}}失败" };
129095
+ const retry = { "confirm_with_model": "重试任务(可切换模型)", "confirm_cloud_with_model": "重试云端任务(可切换模型)", "select_model": "选择重试模型", "load_models_failed": "加载模型列表失败", "no_models_available": "暂无可用模型,请先在设置中配置模型", "model": { "lite": "Fit Lite", "pro": "Fit Pro", "ultra": "Fit Ultra" } };
128816
129096
  const task_type$4 = { "cloud": "云端任务", "local": "本地任务" };
128817
129097
  const zhList = {
128818
129098
  columns: columns$4,
@@ -128820,6 +129100,7 @@ const zhList = {
128820
129100
  actions: actions$d,
128821
129101
  confirmations: confirmations$9,
128822
129102
  messages: messages$e,
129103
+ retry,
128823
129104
  task_type: task_type$4
128824
129105
  };
128825
129106
  const dragger$4 = { "text": "拖拽文件到此处或点击下方按钮选择文件", "hint": "支持单个或批量上传 PDF/JPG/PNG 文件", "hint_local": "支持 PDF 及图片文件。登录后可享免费转换并且支持更多文件类型", "hint_logged_in_non_cloud": "当前仅支持PDF或图片文件,选择Markdown.FIt的云端模型可支持Office类文件", "hint_cloud_logged_in": "支持 PDF、图片及 Office 文件 (Word/Excel/PPT)", "login_hint": "登录后可享免费转换并且支持更多文件类型", "button": "选择文件" };
@@ -128851,7 +129132,7 @@ const zhProvider = {
128851
129132
  const tabs$4 = { "account": "账户", "model_service": "模型服务", "about": "关于我们" };
128852
129133
  const about$4 = { "subtitle": "一款基于大模型视觉识别的高质量PDF转Markdown工具", "buttons": { "website": "官方网址", "license": "许可协议", "feedback": "反馈意见", "contact": "联系邮件" } };
128853
129134
  const update$4 = { "check_for_updates": "检查更新", "checking": "正在检查更新...", "available": "发现新版本", "downloading": "正在下载更新...", "ready": "更新已就绪", "restart_to_update": "重启并更新", "up_to_date": "已是最新版本", "error": "检查更新失败", "retry": "重试" };
128854
- const preview$4 = { "back": "返回", "download": "下载 .md", "delete": "删除", "cancel": "取消", "retry": "重试", "retry_failed": "重试失败页", "retry_all": "全部重试", "more_actions": "更多操作", "regenerate": "重新生成", "regenerate_tooltip": "重新生成当前页", "copy_markdown": "复制 Markdown", "copy_markdown_tooltip": "复制当前页 Markdown", "copy_markdown_success": "Markdown 已复制", "copy_markdown_failed": "复制 Markdown 失败", "copy_image": "复制图片", "copy_image_tooltip": "复制当前页图片", "copy_image_success": "图片已复制", "copy_image_failed": "复制图片失败", "confirm_delete": "确认删除", "confirm_delete_content": "确定要删除此任务吗?此操作不可恢复。", "delete_success": "删除成功", "delete_failed": "删除失败", "confirm_cancel": "确认取消", "confirm_cancel_content": "确定要取消此任务吗?", "cancel_success": "取消成功", "cancel_failed": "取消失败", "confirm_retry": "确认重试", "confirm_retry_content": "确定要重试此任务吗?", "confirm_retry_failed": "确认重试失败页", "confirm_retry_failed_content": "确定要重试所有失败的页面吗?", "retry_success": "任务已加入重试队列", "retry_failed_success": "{{count}} 个失败页面已加入重试队列", "page_retry_success": "页面已加入重试队列", "page_retry_failed": "重试失败", "fetch_task_failed": "获取任务信息失败", "fetch_page_failed": "获取页面详情失败", "task_deleted": "任务已被删除", "task_not_started": "任务尚未开始处理,无法预览", "download_success": "下载成功", "download_failed": "下载失败", "image_load_failed": "图片加载失败或不存在", "status": { "failed": "转换失败", "pending": "等待转换", "processing": "转换中", "completed": "转换完成", "retrying": "重试中" } };
129135
+ const preview$4 = { "back": "返回", "download": "下载 .md", "delete": "删除", "cancel": "取消", "retry": "重试", "retry_failed": "重试失败页", "retry_all": "全部重试", "more_actions": "更多操作", "regenerate": "重新生成", "regenerate_tooltip": "重新生成当前页", "copy_markdown": "复制 Markdown", "copy_markdown_tooltip": "复制当前页 Markdown", "copy_markdown_success": "Markdown 已复制", "copy_markdown_failed": "复制 Markdown 失败", "copy_image": "复制图片", "copy_image_tooltip": "复制当前页图片", "copy_image_success": "图片已复制", "copy_image_failed": "复制图片失败", "confirm_delete": "确认删除", "confirm_delete_content": "确定要删除此任务吗?此操作不可恢复。", "delete_success": "删除成功", "delete_failed": "删除失败", "confirm_cancel": "确认取消", "confirm_cancel_content": "确定要取消此任务吗?", "cancel_success": "取消成功", "cancel_failed": "取消失败", "confirm_retry": "确认重试", "confirm_retry_content": "确定要重试此任务吗?", "confirm_retry_with_model": "重试任务(可切换模型)", "confirm_page_retry_with_model": "重试当前页(可切换模型)", "select_retry_model": "选择重试模型", "confirm_retry_failed": "确认重试失败页", "confirm_retry_failed_content": "确定要重试所有失败的页面吗?", "retry_success": "任务已加入重试队列", "retry_failed_success": "{{count}} 个失败页面已加入重试队列", "page_retry_success": "页面已加入重试队列", "page_retry_failed": "重试失败", "load_models_failed": "加载模型列表失败", "no_models_available": "暂无可用模型,请先在设置中配置模型", "fetch_task_failed": "获取任务信息失败", "fetch_page_failed": "获取页面详情失败", "task_deleted": "任务已被删除", "task_not_started": "任务尚未开始处理,无法预览", "download_success": "下载成功", "download_failed": "下载失败", "image_load_failed": "图片加载失败或不存在", "status": { "failed": "转换失败", "pending": "等待转换", "processing": "转换中", "completed": "转换完成", "retrying": "重试中" } };
128855
129136
  const zhSettings = {
128856
129137
  tabs: tabs$4,
128857
129138
  about: about$4,
@@ -128894,6 +129175,9 @@ const cancel_failed$4 = "取消任务失败";
128894
129175
  const cancel_task$4 = "取消";
128895
129176
  const confirm_retry$4 = "重试任务";
128896
129177
  const confirm_retry_content$4 = "这将创建一个新任务,确定继续吗?";
129178
+ const confirm_retry_with_model = "重试任务(可切换模型)";
129179
+ const confirm_page_retry_with_model = "重试当前页(可切换模型)";
129180
+ const select_retry_model = "选择重试模型";
128897
129181
  const retry_success$4 = "重试已开始";
128898
129182
  const retry_failed$4 = "重试失败";
128899
129183
  const retry_all$4 = "全部重试";
@@ -128921,6 +129205,7 @@ const copy_image$4 = "复制图片";
128921
129205
  const copy_image_tooltip$4 = "复制当前页图片";
128922
129206
  const copy_image_success$4 = "图片已复制";
128923
129207
  const copy_image_failed$4 = "复制图片失败";
129208
+ const retry_model = { "lite": "Fit Lite(约10积分/页)", "pro": "Fit Pro(约20积分/页)", "ultra": "Fit Ultra(约60积分/页)" };
128924
129209
  const page_status$4 = { "pending": "待处理", "processing": "处理中", "completed": "已完成", "failed": "失败" };
128925
129210
  const zhCloudPreview = {
128926
129211
  back: back$4,
@@ -128937,6 +129222,9 @@ const zhCloudPreview = {
128937
129222
  cancel_task: cancel_task$4,
128938
129223
  confirm_retry: confirm_retry$4,
128939
129224
  confirm_retry_content: confirm_retry_content$4,
129225
+ confirm_retry_with_model,
129226
+ confirm_page_retry_with_model,
129227
+ select_retry_model,
128940
129228
  retry_success: retry_success$4,
128941
129229
  retry_failed: retry_failed$4,
128942
129230
  retry_all: retry_all$4,
@@ -128964,6 +129252,7 @@ const zhCloudPreview = {
128964
129252
  copy_image_tooltip: copy_image_tooltip$4,
128965
129253
  copy_image_success: copy_image_success$4,
128966
129254
  copy_image_failed: copy_image_failed$4,
129255
+ retry_model,
128967
129256
  page_status: page_status$4
128968
129257
  };
128969
129258
  const navigation$3 = { "home": "ホーム", "list": "リスト", "settings": "設定" };
@@ -129944,9 +130233,12 @@ const CloudProvider = ({ children }) => {
129944
130233
  return { success: false, error: error instanceof Error ? error.message : String(error) };
129945
130234
  }
129946
130235
  }, [isAuthenticated, refreshCredits]);
129947
- const retryTask = reactExports.useCallback(async (id) => {
130236
+ const retryTask = reactExports.useCallback(async (id, model) => {
129948
130237
  if (!isAuthenticated) return { success: false, error: "User not signed in" };
129949
130238
  try {
130239
+ if (model) {
130240
+ return await window.api.cloud.retryTask({ id, model });
130241
+ }
129950
130242
  return await window.api.cloud.retryTask(id);
129951
130243
  } catch (error) {
129952
130244
  return { success: false, error: error instanceof Error ? error.message : String(error) };
@@ -129960,9 +130252,12 @@ const CloudProvider = ({ children }) => {
129960
130252
  return { success: false, error: error instanceof Error ? error.message : String(error) };
129961
130253
  }
129962
130254
  }, [isAuthenticated]);
129963
- const retryPage = reactExports.useCallback(async (taskId, pageNumber) => {
130255
+ const retryPage = reactExports.useCallback(async (taskId, pageNumber, model) => {
129964
130256
  if (!isAuthenticated) return { success: false, error: "User not signed in" };
129965
130257
  try {
130258
+ if (model) {
130259
+ return await window.api.cloud.retryPage({ taskId, pageNumber, model });
130260
+ }
129966
130261
  return await window.api.cloud.retryPage({ taskId, pageNumber });
129967
130262
  } catch (error) {
129968
130263
  return { success: false, error: error instanceof Error ? error.message : String(error) };
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>MarkPDFdown</title>
8
- <script type="module" crossorigin src="./assets/index-CNUNN0iI.js"></script>
8
+ <script type="module" crossorigin src="./assets/index-BDl2RVlc.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="./assets/index-DXcyx2Q8.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markpdfdown",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "A high-quality PDF to Markdown tool based on large language model visual recognition.",
5
5
  "author": "MarkPDFdown",
6
6
  "main": "dist/main/index.js",
@@ -32,7 +32,7 @@
32
32
  "migrate:reset": "prisma migrate reset --schema=./src/core/infrastructure/db/schema.prisma",
33
33
  "generate": "prisma generate --schema=./src/core/infrastructure/db/schema.prisma",
34
34
  "logo": "electron-icon-builder --input=./src/renderer/assets/logo.png --output=./public",
35
- "test": "vitest",
35
+ "test": "vitest run",
36
36
  "test:unit": "vitest run --config vitest.config.ts",
37
37
  "test:renderer": "vitest run --config vitest.config.renderer.ts",
38
38
  "test:watch": "vitest watch",