markpdfdown 0.4.3 → 0.4.6

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,8 @@ 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"];
71132
+ const cloudProgressCache = /* @__PURE__ */ new Map();
71131
71133
  const List = () => {
71132
71134
  const { message: message2, modal } = App$1.useApp();
71133
71135
  const { t: t2 } = useTranslation("list");
@@ -71146,6 +71148,45 @@ const List = () => {
71146
71148
  const paginationRef = reactExports.useRef(pagination);
71147
71149
  paginationRef.current = pagination;
71148
71150
  const MAX_FETCH_ITEMS = 100;
71151
+ const buildLocalModelValue = (modelId, providerId) => `${modelId}@${providerId}`;
71152
+ const parseLocalModelValue = (value) => {
71153
+ const separatorIndex = value.lastIndexOf("@");
71154
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
71155
+ return null;
71156
+ }
71157
+ const modelId = value.slice(0, separatorIndex);
71158
+ const providerId = Number(value.slice(separatorIndex + 1));
71159
+ if (!modelId || !Number.isInteger(providerId)) {
71160
+ return null;
71161
+ }
71162
+ return {
71163
+ modelId,
71164
+ providerId
71165
+ };
71166
+ };
71167
+ const loadLocalModelOptions = reactExports.useCallback(async () => {
71168
+ const result = await window.api.model.getAll();
71169
+ if (!result.success || !result.data) {
71170
+ throw new Error(result.error || t2("retry.load_models_failed"));
71171
+ }
71172
+ const modelGroups = result.data;
71173
+ return modelGroups.flatMap(
71174
+ (group) => group.models.map((model) => ({
71175
+ value: buildLocalModelValue(model.id, group.provider),
71176
+ label: `${model.name} | ${group.providerName}`
71177
+ }))
71178
+ );
71179
+ }, [t2]);
71180
+ const parseCloudModelTier = (record) => {
71181
+ const explicitTier = record?.model_tier;
71182
+ if (explicitTier && CLOUD_MODEL_TIERS$1.includes(explicitTier)) {
71183
+ return explicitTier;
71184
+ }
71185
+ const modelName = (record.model_name || "").toLowerCase();
71186
+ if (modelName.includes("ultra")) return "ultra";
71187
+ if (modelName.includes("pro")) return "pro";
71188
+ return "lite";
71189
+ };
71149
71190
  const fetchTasks = reactExports.useCallback(async (page = 1, pageSize = 10) => {
71150
71191
  setLoading(true);
71151
71192
  try {
@@ -71188,7 +71229,50 @@ const List = () => {
71188
71229
  combinedList.sort((a2, b) => getTimestamp(b) - getTimestamp(a2));
71189
71230
  const startIndex = (page - 1) * pageSize;
71190
71231
  const paginatedList = combinedList.slice(startIndex, startIndex + pageSize);
71191
- setData(paginatedList);
71232
+ setData((prevData) => {
71233
+ const existingMap = /* @__PURE__ */ new Map();
71234
+ for (const item of prevData) {
71235
+ if (item.isCloud && item.id) {
71236
+ existingMap.set(item.id, item);
71237
+ }
71238
+ }
71239
+ return paginatedList.map((task) => {
71240
+ if (task.isCloud && task.id) {
71241
+ const fromState = existingMap.get(task.id);
71242
+ const fromCache = cloudProgressCache.get(task.id);
71243
+ const maxProgress = Math.max(
71244
+ task.progress || 0,
71245
+ fromState?.progress || 0,
71246
+ fromCache?.progress || 0
71247
+ );
71248
+ const maxCompleted = Math.max(
71249
+ task.completed_count || 0,
71250
+ fromState?.completed_count || 0,
71251
+ fromCache?.completed_count || 0
71252
+ );
71253
+ const maxFailed = Math.max(
71254
+ task.failed_count || 0,
71255
+ fromState?.failed_count || 0,
71256
+ fromCache?.failed_count || 0
71257
+ );
71258
+ cloudProgressCache.set(task.id, {
71259
+ progress: maxProgress,
71260
+ completed_count: maxCompleted,
71261
+ failed_count: maxFailed,
71262
+ status: task.status ?? fromCache?.status ?? 0
71263
+ });
71264
+ if (maxProgress > (task.progress || 0) || maxCompleted > (task.completed_count || 0)) {
71265
+ return {
71266
+ ...task,
71267
+ progress: maxProgress,
71268
+ completed_count: maxCompleted,
71269
+ failed_count: maxFailed
71270
+ };
71271
+ }
71272
+ }
71273
+ return task;
71274
+ });
71275
+ });
71192
71276
  setPagination((prev2) => ({
71193
71277
  ...prev2,
71194
71278
  current: page,
@@ -71281,9 +71365,13 @@ const List = () => {
71281
71365
  case "page_completed": {
71282
71366
  const pageNumber = data2.page;
71283
71367
  const totalPages = data2.total_pages || task.pages || 1;
71284
- const completed = Math.max(task.completed_count || 0, pageNumber || 0);
71285
- task.completed_count = completed;
71286
- task.progress = Math.round(completed / totalPages * 100);
71368
+ if (task.status === 8) {
71369
+ task.completed_count = (task.completed_count || 0) + 1;
71370
+ task.failed_count = Math.max(0, (task.failed_count || 0) - 1);
71371
+ } else {
71372
+ task.completed_count = Math.max(task.completed_count || 0, pageNumber || 0);
71373
+ }
71374
+ task.progress = Math.round((task.completed_count || 0) / totalPages * 100);
71287
71375
  task.status = 3;
71288
71376
  break;
71289
71377
  }
@@ -71316,6 +71404,19 @@ const List = () => {
71316
71404
  return prevData;
71317
71405
  }
71318
71406
  newData[index2] = task;
71407
+ if (task.id) {
71408
+ const terminalStatuses = [0, 6, 7, 8];
71409
+ if (terminalStatuses.includes(task.status ?? -1)) {
71410
+ cloudProgressCache.delete(task.id);
71411
+ } else {
71412
+ cloudProgressCache.set(task.id, {
71413
+ progress: task.progress || 0,
71414
+ completed_count: task.completed_count || 0,
71415
+ failed_count: task.failed_count || 0,
71416
+ status: task.status ?? 0
71417
+ });
71418
+ }
71419
+ }
71319
71420
  return newData;
71320
71421
  });
71321
71422
  };
@@ -71408,8 +71509,62 @@ const List = () => {
71408
71509
  }
71409
71510
  });
71410
71511
  };
71411
- const handleRetryTask = (id) => {
71412
- handleUpdateTaskStatus(id, 1, t2("actions.retry"));
71512
+ const handleRetryTask = async (task) => {
71513
+ if (!task.id) return;
71514
+ try {
71515
+ const modelOptions = await loadLocalModelOptions();
71516
+ if (modelOptions.length === 0) {
71517
+ message2.error(t2("retry.no_models_available"));
71518
+ return;
71519
+ }
71520
+ const defaultModelValue = task.model && task.provider !== void 0 && task.provider >= 0 ? buildLocalModelValue(task.model, task.provider) : modelOptions[0].value;
71521
+ let selectedModelValue = modelOptions.some((opt) => opt.value === defaultModelValue) ? defaultModelValue : modelOptions[0].value;
71522
+ modal.confirm({
71523
+ title: t2("retry.confirm_with_model"),
71524
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
71525
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("retry.select_model") }),
71526
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
71527
+ Select,
71528
+ {
71529
+ style: { width: "100%" },
71530
+ options: modelOptions,
71531
+ defaultValue: selectedModelValue,
71532
+ onChange: (value) => {
71533
+ selectedModelValue = value;
71534
+ }
71535
+ }
71536
+ )
71537
+ ] }),
71538
+ okText: t2("confirmations.ok"),
71539
+ cancelText: t2("confirmations.cancel"),
71540
+ onOk: async () => {
71541
+ const parsedModel = parseLocalModelValue(selectedModelValue);
71542
+ if (!parsedModel) {
71543
+ message2.error(t2("messages.action_failed", { action: t2("actions.retry") }));
71544
+ return;
71545
+ }
71546
+ const { modelId, providerId } = parsedModel;
71547
+ try {
71548
+ const result = await window.api.task.retry({
71549
+ taskId: task.id,
71550
+ providerId,
71551
+ modelId
71552
+ });
71553
+ if (result.success) {
71554
+ message2.success(t2("messages.action_success", { action: t2("actions.retry") }));
71555
+ fetchTasks(pagination.current, pagination.pageSize);
71556
+ } else {
71557
+ message2.error(result.error || t2("messages.action_failed", { action: t2("actions.retry") }));
71558
+ }
71559
+ } catch (error) {
71560
+ console.error("Failed to retry local task:", error);
71561
+ message2.error(t2("messages.action_failed", { action: t2("actions.retry") }));
71562
+ }
71563
+ }
71564
+ });
71565
+ } catch (error) {
71566
+ message2.error(error instanceof Error ? error.message : t2("retry.load_models_failed"));
71567
+ }
71413
71568
  };
71414
71569
  const handleCancelTask = (id) => {
71415
71570
  handleUpdateTaskStatus(id, 7, t2("actions.cancel"));
@@ -71436,16 +71591,34 @@ const List = () => {
71436
71591
  }
71437
71592
  });
71438
71593
  };
71439
- const handleCloudRetryTask = async (id) => {
71594
+ const handleCloudRetryTask = async (task) => {
71440
71595
  if (!cloudContext) return;
71596
+ if (!task.id) return;
71597
+ let selectedModel = parseCloudModelTier(task);
71441
71598
  modal.confirm({
71442
- title: t2("confirmations.cancel_title", { action: t2("actions.retry") }),
71443
- content: t2("confirmations.cancel_content", { action: t2("actions.retry") }),
71599
+ title: t2("retry.confirm_cloud_with_model"),
71600
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
71601
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("retry.select_model") }),
71602
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
71603
+ Select,
71604
+ {
71605
+ style: { width: "100%" },
71606
+ options: CLOUD_MODEL_TIERS$1.map((tier) => ({
71607
+ value: tier,
71608
+ label: t2(`retry.model.${tier}`)
71609
+ })),
71610
+ defaultValue: selectedModel,
71611
+ onChange: (value) => {
71612
+ selectedModel = value;
71613
+ }
71614
+ }
71615
+ )
71616
+ ] }),
71444
71617
  okText: t2("confirmations.ok"),
71445
71618
  cancelText: t2("confirmations.cancel"),
71446
71619
  onOk: async () => {
71447
71620
  try {
71448
- const result = await cloudContext.retryTask(id);
71621
+ const result = await cloudContext.retryTask(task.id, selectedModel);
71449
71622
  if (result.success) {
71450
71623
  message2.success(t2("messages.action_success", { action: t2("actions.retry") }));
71451
71624
  fetchTasks(pagination.current, pagination.pageSize);
@@ -71645,7 +71818,7 @@ const List = () => {
71645
71818
  {
71646
71819
  type: "warning",
71647
71820
  style: { cursor: "pointer" },
71648
- onClick: () => record.id && (isCloud ? handleCloudRetryTask(record.id) : handleRetryTask(record.id)),
71821
+ onClick: () => record.id && (isCloud ? handleCloudRetryTask(record) : handleRetryTask(record)),
71649
71822
  children: t2("actions.retry")
71650
71823
  }
71651
71824
  );
@@ -125336,6 +125509,35 @@ const Preview = () => {
125336
125509
  const [imageError, setImageError] = reactExports.useState(false);
125337
125510
  const [retrying, setRetrying] = reactExports.useState(false);
125338
125511
  const [retryingFailed, setRetryingFailed] = reactExports.useState(false);
125512
+ const buildModelValue = (modelId, providerId) => `${modelId}@${providerId}`;
125513
+ const parseModelValue = (value) => {
125514
+ const separatorIndex = value.lastIndexOf("@");
125515
+ if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
125516
+ return null;
125517
+ }
125518
+ const modelId = value.slice(0, separatorIndex);
125519
+ const providerId = Number(value.slice(separatorIndex + 1));
125520
+ if (!modelId || !Number.isInteger(providerId)) {
125521
+ return null;
125522
+ }
125523
+ return {
125524
+ modelId,
125525
+ providerId
125526
+ };
125527
+ };
125528
+ const loadLocalModelOptions = reactExports.useCallback(async () => {
125529
+ const result = await window.api.model.getAll();
125530
+ if (!result.success || !result.data) {
125531
+ throw new Error(result.error || t2("preview.load_models_failed"));
125532
+ }
125533
+ const modelGroups = result.data;
125534
+ return modelGroups.flatMap(
125535
+ (group) => group.models.map((model) => ({
125536
+ value: buildModelValue(model.id, group.provider),
125537
+ label: `${model.name} | ${group.providerName}`
125538
+ }))
125539
+ );
125540
+ }, [t2]);
125339
125541
  const fetchTask = reactExports.useCallback(async () => {
125340
125542
  if (!id) return;
125341
125543
  try {
@@ -125489,26 +125691,63 @@ const Preview = () => {
125489
125691
  });
125490
125692
  };
125491
125693
  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"));
125694
+ if (!id || !task) return;
125695
+ try {
125696
+ const modelOptions = await loadLocalModelOptions();
125697
+ if (modelOptions.length === 0) {
125698
+ message2.error(t2("preview.no_models_available"));
125699
+ return;
125700
+ }
125701
+ const defaultModelValue = buildModelValue(task.model || "", task.provider || 0);
125702
+ let selectedModelValue = modelOptions.some((opt) => opt.value === defaultModelValue) ? defaultModelValue : modelOptions[0].value;
125703
+ modal.confirm({
125704
+ title: t2("preview.confirm_retry_with_model"),
125705
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
125706
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("preview.select_retry_model") }),
125707
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
125708
+ Select,
125709
+ {
125710
+ style: { width: "100%" },
125711
+ options: modelOptions,
125712
+ defaultValue: selectedModelValue,
125713
+ onChange: (value) => {
125714
+ selectedModelValue = value;
125715
+ }
125716
+ }
125717
+ )
125718
+ ] }),
125719
+ okText: tCommon("common.confirm"),
125720
+ cancelText: tCommon("common.cancel"),
125721
+ onOk: async () => {
125722
+ try {
125723
+ const shouldOverride = selectedModelValue !== defaultModelValue;
125724
+ let overridePayload = null;
125725
+ if (shouldOverride) {
125726
+ const parsedModel = parseModelValue(selectedModelValue);
125727
+ if (!parsedModel) {
125728
+ message2.error(t2("preview.retry_failed"));
125729
+ return;
125730
+ }
125731
+ overridePayload = parsedModel;
125732
+ }
125733
+ const result = await window.api.task.retry({
125734
+ taskId: id,
125735
+ ...overridePayload || {}
125736
+ });
125737
+ if (result.success) {
125738
+ message2.success(t2("preview.retry_success"));
125739
+ } else {
125740
+ message2.error(result.error || t2("preview.retry_failed"));
125741
+ }
125742
+ } catch (error) {
125743
+ console.error("重试失败:", error);
125744
+ message2.error(t2("preview.retry_failed"));
125505
125745
  }
125506
- } catch (error) {
125507
- console.error("重试失败:", error);
125508
- message2.error(t2("preview.retry_failed"));
125509
125746
  }
125510
- }
125511
- });
125747
+ });
125748
+ } catch (error) {
125749
+ message2.error(error instanceof Error ? error.message : t2("preview.load_models_failed"));
125750
+ }
125512
125751
  };
125513
125752
  const handleRetryFailed = async () => {
125514
125753
  if (!id) return;
@@ -125540,20 +125779,64 @@ const Preview = () => {
125540
125779
  };
125541
125780
  const handleRetryPage = async () => {
125542
125781
  if (!taskDetail?.id) return;
125543
- setRetrying(true);
125544
125782
  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"));
125783
+ const modelOptions = await loadLocalModelOptions();
125784
+ if (modelOptions.length === 0) {
125785
+ message2.error(t2("preview.no_models_available"));
125786
+ return;
125551
125787
  }
125788
+ const defaultModelValue = buildModelValue(taskDetail.model, taskDetail.provider);
125789
+ let selectedModelValue = modelOptions.some((opt) => opt.value === defaultModelValue) ? defaultModelValue : modelOptions[0].value;
125790
+ modal.confirm({
125791
+ title: t2("preview.confirm_page_retry_with_model"),
125792
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
125793
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("preview.select_retry_model") }),
125794
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
125795
+ Select,
125796
+ {
125797
+ style: { width: "100%" },
125798
+ options: modelOptions,
125799
+ defaultValue: selectedModelValue,
125800
+ onChange: (value) => {
125801
+ selectedModelValue = value;
125802
+ }
125803
+ }
125804
+ )
125805
+ ] }),
125806
+ okText: tCommon("common.confirm"),
125807
+ cancelText: tCommon("common.cancel"),
125808
+ onOk: async () => {
125809
+ setRetrying(true);
125810
+ try {
125811
+ const shouldOverride = selectedModelValue !== defaultModelValue;
125812
+ let overridePayload = null;
125813
+ if (shouldOverride) {
125814
+ const parsedModel = parseModelValue(selectedModelValue);
125815
+ if (!parsedModel) {
125816
+ message2.error(t2("preview.page_retry_failed"));
125817
+ return;
125818
+ }
125819
+ overridePayload = parsedModel;
125820
+ }
125821
+ const result = await window.api.taskDetail.retry(
125822
+ overridePayload ? { pageId: taskDetail.id, ...overridePayload } : { pageId: taskDetail.id }
125823
+ );
125824
+ if (result.success) {
125825
+ message2.success(t2("preview.page_retry_success"));
125826
+ fetchPageDetail(currentPage);
125827
+ } else {
125828
+ message2.error(result.error || t2("preview.page_retry_failed"));
125829
+ }
125830
+ } catch (error) {
125831
+ console.error("Failed to retry page:", error);
125832
+ message2.error(t2("preview.page_retry_failed"));
125833
+ } finally {
125834
+ setRetrying(false);
125835
+ }
125836
+ }
125837
+ });
125552
125838
  } catch (error) {
125553
- console.error("Failed to retry page:", error);
125554
- message2.error(t2("preview.page_retry_failed"));
125555
- } finally {
125556
- setRetrying(false);
125839
+ message2.error(error instanceof Error ? error.message : t2("preview.load_models_failed"));
125557
125840
  }
125558
125841
  };
125559
125842
  const handleCopyMarkdown = async () => {
@@ -125703,7 +125986,7 @@ const Preview = () => {
125703
125986
  disabled: retryingFailed
125704
125987
  });
125705
125988
  }
125706
- if (status2 === 0) {
125989
+ if (status2 === 0 || status2 === 6) {
125707
125990
  menuItems.push({
125708
125991
  key: "retry_all",
125709
125992
  icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$M, {}),
@@ -125907,6 +126190,7 @@ const Preview = () => {
125907
126190
  };
125908
126191
  const { Text } = Typography;
125909
126192
  const dedupeAndSortPages = (pageItems) => Array.from(new Map(pageItems.map((page) => [page.page, page])).values()).sort((a2, b) => a2.page - b.page);
126193
+ const CLOUD_MODEL_TIERS = ["lite", "pro", "ultra"];
125910
126194
  const CloudPreview = () => {
125911
126195
  const { id } = useParams();
125912
126196
  const navigate = useNavigate();
@@ -126077,13 +126361,20 @@ const CloudPreview = () => {
126077
126361
  }
126078
126362
  return [...prev2, { page, status: 2, status_name: "COMPLETED", markdown: markdown2, width_mm: 210, height_mm: 297 }];
126079
126363
  });
126080
- setTask((prev2) => prev2 ? {
126081
- ...prev2,
126082
- pages_completed: (prev2.pages_completed || 0) + 1
126083
- } : null);
126364
+ setTask((prev2) => {
126365
+ if (!prev2) return null;
126366
+ const updates = {
126367
+ pages_completed: (prev2.pages_completed || 0) + 1
126368
+ };
126369
+ if (prev2.status === 8) {
126370
+ updates.pages_failed = Math.max(0, (prev2.pages_failed || 0) - 1);
126371
+ }
126372
+ return { ...prev2, ...updates };
126373
+ });
126084
126374
  break;
126085
126375
  }
126086
- case "page_started": {
126376
+ case "page_started":
126377
+ case "page_retry_started": {
126087
126378
  const { page } = event.data;
126088
126379
  setPages((prev2) => {
126089
126380
  const idx = prev2.findIndex((p2) => p2.page === page);
@@ -126121,6 +126412,9 @@ const CloudPreview = () => {
126121
126412
  pages_completed: data.pages_completed,
126122
126413
  pages_failed: data.pages_failed
126123
126414
  } : null);
126415
+ if (data.pages_failed === 0) {
126416
+ setPages((prev2) => prev2.map((p2) => p2.status !== 2 ? { ...p2, status: 2, status_name: "COMPLETED" } : p2));
126417
+ }
126124
126418
  break;
126125
126419
  }
126126
126420
  case "error": {
@@ -126200,13 +126494,32 @@ const CloudPreview = () => {
126200
126494
  };
126201
126495
  const handleRetryTask = async () => {
126202
126496
  if (!id || !cloudContext) return;
126497
+ const options = CLOUD_MODEL_TIERS.map((tier) => ({
126498
+ value: tier,
126499
+ label: t2(`retry_model.${tier}`)
126500
+ }));
126501
+ const taskTier = task?.model_tier || "lite";
126502
+ let selectedModel = CLOUD_MODEL_TIERS.includes(taskTier) ? taskTier : "lite";
126203
126503
  modal.confirm({
126204
- title: t2("confirm_retry"),
126205
- content: t2("confirm_retry_content"),
126504
+ title: t2("confirm_retry_with_model"),
126505
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
126506
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("select_retry_model") }),
126507
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
126508
+ Select,
126509
+ {
126510
+ style: { width: "100%" },
126511
+ options,
126512
+ defaultValue: selectedModel,
126513
+ onChange: (value) => {
126514
+ selectedModel = value;
126515
+ }
126516
+ }
126517
+ )
126518
+ ] }),
126206
126519
  okText: tCommon("common.confirm"),
126207
126520
  cancelText: tCommon("common.cancel"),
126208
126521
  onOk: async () => {
126209
- const result = await cloudContext.retryTask(id);
126522
+ const result = await cloudContext.retryTask(id, selectedModel);
126210
126523
  if (result.success && result.data) {
126211
126524
  message2.success(t2("retry_success"));
126212
126525
  navigate(`/list/cloud-preview/${result.data.task_id}`);
@@ -126218,28 +126531,55 @@ const CloudPreview = () => {
126218
126531
  };
126219
126532
  const handleRetryPage = async () => {
126220
126533
  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;
126534
+ const options = CLOUD_MODEL_TIERS.map((tier) => ({
126535
+ value: tier,
126536
+ label: t2(`retry_model.${tier}`)
126537
+ }));
126538
+ const taskTier = task?.model_tier || "lite";
126539
+ let selectedModel = CLOUD_MODEL_TIERS.includes(taskTier) ? taskTier : "lite";
126540
+ modal.confirm({
126541
+ title: t2("confirm_page_retry_with_model"),
126542
+ content: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { marginTop: 8 }, children: [
126543
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { marginBottom: 8 }, children: t2("select_retry_model") }),
126544
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
126545
+ Select,
126546
+ {
126547
+ style: { width: "100%" },
126548
+ options,
126549
+ defaultValue: selectedModel,
126550
+ onChange: (value) => {
126551
+ selectedModel = value;
126552
+ }
126232
126553
  }
126233
- return prev2;
126234
- });
126235
- } else {
126236
- message2.error(result.error || t2("page_retry_failed"));
126554
+ )
126555
+ ] }),
126556
+ okText: tCommon("common.confirm"),
126557
+ cancelText: tCommon("common.cancel"),
126558
+ onOk: async () => {
126559
+ setRetrying(true);
126560
+ try {
126561
+ const result = await cloudContext.retryPage(id, currentPage, selectedModel);
126562
+ if (result.success) {
126563
+ message2.success(t2("page_retry_success"));
126564
+ setPages((prev2) => {
126565
+ const idx = prev2.findIndex((p2) => p2.page === currentPage);
126566
+ if (idx >= 0) {
126567
+ const updated = [...prev2];
126568
+ updated[idx] = { ...updated[idx], status: 1 };
126569
+ return updated;
126570
+ }
126571
+ return prev2;
126572
+ });
126573
+ } else {
126574
+ message2.error(result.error || t2("page_retry_failed"));
126575
+ }
126576
+ } catch {
126577
+ message2.error(t2("page_retry_failed"));
126578
+ } finally {
126579
+ setRetrying(false);
126580
+ }
126237
126581
  }
126238
- } catch {
126239
- message2.error(t2("page_retry_failed"));
126240
- } finally {
126241
- setRetrying(false);
126242
- }
126582
+ });
126243
126583
  };
126244
126584
  const handleRetryFailed = async () => {
126245
126585
  if (!id || !cloudContext) return;
@@ -126464,7 +126804,7 @@ const CloudPreview = () => {
126464
126804
  disabled: retryingFailed
126465
126805
  });
126466
126806
  }
126467
- if (status2 === 0) {
126807
+ if (status2 === 0 || status2 === 6) {
126468
126808
  menuItems.push({
126469
126809
  key: "retry_all",
126470
126810
  icon: /* @__PURE__ */ jsxRuntimeExports.jsx(RefIcon$M, {}),
@@ -128635,6 +128975,7 @@ const status$a = { "pending": "Pending", "initializing": "Initializing", "proces
128635
128975
  const actions$g = { "view": "View", "cancel": "Cancel", "retry": "Retry", "delete": "Delete" };
128636
128976
  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
128977
  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" };
128978
+ 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
128979
  const task_type$5 = { "cloud": "Cloud Task", "local": "Local Task" };
128639
128980
  const enList = {
128640
128981
  columns: columns$5,
@@ -128642,6 +128983,7 @@ const enList = {
128642
128983
  actions: actions$g,
128643
128984
  confirmations: confirmations$b,
128644
128985
  messages: messages$h,
128986
+ retry: retry$1,
128645
128987
  task_type: task_type$5
128646
128988
  };
128647
128989
  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 +129015,7 @@ const enProvider = {
128673
129015
  const tabs$5 = { "account": "Account", "model_service": "Model Service", "about": "About Us" };
128674
129016
  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
129017
  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" } };
129018
+ 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
129019
  const enSettings = {
128678
129020
  tabs: tabs$5,
128679
129021
  about: about$5,
@@ -128716,6 +129058,9 @@ const cancel_failed$5 = "Failed to cancel task";
128716
129058
  const cancel_task$5 = "Cancel";
128717
129059
  const confirm_retry$5 = "Retry Task";
128718
129060
  const confirm_retry_content$5 = "This will create a new task. Are you sure?";
129061
+ const confirm_retry_with_model$1 = "Retry Task (Choose Model)";
129062
+ const confirm_page_retry_with_model$1 = "Retry Current Page (Choose Model)";
129063
+ const select_retry_model$1 = "Select retry model";
128719
129064
  const retry_success$5 = "Retry started";
128720
129065
  const retry_failed$5 = "Retry failed";
128721
129066
  const retry_all$5 = "Retry All";
@@ -128743,6 +129088,7 @@ const copy_image$5 = "Copy Image";
128743
129088
  const copy_image_tooltip$5 = "Copy current page image";
128744
129089
  const copy_image_success$5 = "Image copied";
128745
129090
  const copy_image_failed$5 = "Failed to copy image";
129091
+ const retry_model$1 = { "lite": "Fit Lite (~10 credits/page)", "pro": "Fit Pro (~20 credits/page)", "ultra": "Fit Ultra (~60 credits/page)" };
128746
129092
  const page_status$5 = { "pending": "Pending", "processing": "Processing", "completed": "Completed", "failed": "Failed" };
128747
129093
  const enCloudPreview = {
128748
129094
  back: back$5,
@@ -128759,6 +129105,9 @@ const enCloudPreview = {
128759
129105
  cancel_task: cancel_task$5,
128760
129106
  confirm_retry: confirm_retry$5,
128761
129107
  confirm_retry_content: confirm_retry_content$5,
129108
+ confirm_retry_with_model: confirm_retry_with_model$1,
129109
+ confirm_page_retry_with_model: confirm_page_retry_with_model$1,
129110
+ select_retry_model: select_retry_model$1,
128762
129111
  retry_success: retry_success$5,
128763
129112
  retry_failed: retry_failed$5,
128764
129113
  retry_all: retry_all$5,
@@ -128786,6 +129135,7 @@ const enCloudPreview = {
128786
129135
  copy_image_tooltip: copy_image_tooltip$5,
128787
129136
  copy_image_success: copy_image_success$5,
128788
129137
  copy_image_failed: copy_image_failed$5,
129138
+ retry_model: retry_model$1,
128789
129139
  page_status: page_status$5
128790
129140
  };
128791
129141
  const navigation$4 = { "home": "主页", "list": "列表", "settings": "设置" };
@@ -128813,6 +129163,7 @@ const status$8 = { "pending": "待处理", "initializing": "初始化", "process
128813
129163
  const actions$d = { "view": "查看", "cancel": "取消", "retry": "重试", "delete": "删除" };
128814
129164
  const confirmations$9 = { "delete_title": "确认删除", "delete_content": "确定要删除这个任务吗?此操作不可逆。", "cancel_title": "确认{{action}}", "cancel_content": "确定要{{action}}这个任务吗?", "ok": "确定", "cancel": "取消" };
128815
129165
  const messages$e = { "fetch_failed": "获取任务列表失败", "delete_success": "删除成功", "delete_failed": "删除任务失败", "action_success": "{{action}}成功", "action_failed": "{{action}}失败" };
129166
+ 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
129167
  const task_type$4 = { "cloud": "云端任务", "local": "本地任务" };
128817
129168
  const zhList = {
128818
129169
  columns: columns$4,
@@ -128820,6 +129171,7 @@ const zhList = {
128820
129171
  actions: actions$d,
128821
129172
  confirmations: confirmations$9,
128822
129173
  messages: messages$e,
129174
+ retry,
128823
129175
  task_type: task_type$4
128824
129176
  };
128825
129177
  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 +129203,7 @@ const zhProvider = {
128851
129203
  const tabs$4 = { "account": "账户", "model_service": "模型服务", "about": "关于我们" };
128852
129204
  const about$4 = { "subtitle": "一款基于大模型视觉识别的高质量PDF转Markdown工具", "buttons": { "website": "官方网址", "license": "许可协议", "feedback": "反馈意见", "contact": "联系邮件" } };
128853
129205
  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": "重试中" } };
129206
+ 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
129207
  const zhSettings = {
128856
129208
  tabs: tabs$4,
128857
129209
  about: about$4,
@@ -128894,6 +129246,9 @@ const cancel_failed$4 = "取消任务失败";
128894
129246
  const cancel_task$4 = "取消";
128895
129247
  const confirm_retry$4 = "重试任务";
128896
129248
  const confirm_retry_content$4 = "这将创建一个新任务,确定继续吗?";
129249
+ const confirm_retry_with_model = "重试任务(可切换模型)";
129250
+ const confirm_page_retry_with_model = "重试当前页(可切换模型)";
129251
+ const select_retry_model = "选择重试模型";
128897
129252
  const retry_success$4 = "重试已开始";
128898
129253
  const retry_failed$4 = "重试失败";
128899
129254
  const retry_all$4 = "全部重试";
@@ -128921,6 +129276,7 @@ const copy_image$4 = "复制图片";
128921
129276
  const copy_image_tooltip$4 = "复制当前页图片";
128922
129277
  const copy_image_success$4 = "图片已复制";
128923
129278
  const copy_image_failed$4 = "复制图片失败";
129279
+ const retry_model = { "lite": "Fit Lite(约10积分/页)", "pro": "Fit Pro(约20积分/页)", "ultra": "Fit Ultra(约60积分/页)" };
128924
129280
  const page_status$4 = { "pending": "待处理", "processing": "处理中", "completed": "已完成", "failed": "失败" };
128925
129281
  const zhCloudPreview = {
128926
129282
  back: back$4,
@@ -128937,6 +129293,9 @@ const zhCloudPreview = {
128937
129293
  cancel_task: cancel_task$4,
128938
129294
  confirm_retry: confirm_retry$4,
128939
129295
  confirm_retry_content: confirm_retry_content$4,
129296
+ confirm_retry_with_model,
129297
+ confirm_page_retry_with_model,
129298
+ select_retry_model,
128940
129299
  retry_success: retry_success$4,
128941
129300
  retry_failed: retry_failed$4,
128942
129301
  retry_all: retry_all$4,
@@ -128964,6 +129323,7 @@ const zhCloudPreview = {
128964
129323
  copy_image_tooltip: copy_image_tooltip$4,
128965
129324
  copy_image_success: copy_image_success$4,
128966
129325
  copy_image_failed: copy_image_failed$4,
129326
+ retry_model,
128967
129327
  page_status: page_status$4
128968
129328
  };
128969
129329
  const navigation$3 = { "home": "ホーム", "list": "リスト", "settings": "設定" };
@@ -129944,9 +130304,12 @@ const CloudProvider = ({ children }) => {
129944
130304
  return { success: false, error: error instanceof Error ? error.message : String(error) };
129945
130305
  }
129946
130306
  }, [isAuthenticated, refreshCredits]);
129947
- const retryTask = reactExports.useCallback(async (id) => {
130307
+ const retryTask = reactExports.useCallback(async (id, model) => {
129948
130308
  if (!isAuthenticated) return { success: false, error: "User not signed in" };
129949
130309
  try {
130310
+ if (model) {
130311
+ return await window.api.cloud.retryTask({ id, model });
130312
+ }
129950
130313
  return await window.api.cloud.retryTask(id);
129951
130314
  } catch (error) {
129952
130315
  return { success: false, error: error instanceof Error ? error.message : String(error) };
@@ -129960,9 +130323,12 @@ const CloudProvider = ({ children }) => {
129960
130323
  return { success: false, error: error instanceof Error ? error.message : String(error) };
129961
130324
  }
129962
130325
  }, [isAuthenticated]);
129963
- const retryPage = reactExports.useCallback(async (taskId, pageNumber) => {
130326
+ const retryPage = reactExports.useCallback(async (taskId, pageNumber, model) => {
129964
130327
  if (!isAuthenticated) return { success: false, error: "User not signed in" };
129965
130328
  try {
130329
+ if (model) {
130330
+ return await window.api.cloud.retryPage({ taskId, pageNumber, model });
130331
+ }
129966
130332
  return await window.api.cloud.retryPage({ taskId, pageNumber });
129967
130333
  } catch (error) {
129968
130334
  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-CFJUTNAj.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.6",
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",