| 1 | import type { InboxQueueRequest, InboxQueueResult } from "./inboxQueueCommands"; |
| 2 | import type { InboxTarget } from "./pendingFollowup"; |
| 3 | import type { AppBindings } from "./bridge"; |
| 4 | |
| 5 | // Browser-only fixture using the production component and command contract. |
| 6 | // No live files, sessions, providers, or mutation endpoints are involved. |
| 7 | export function createInboxQueuePreview() { |
| 8 | let revision = 1, paused = false; |
| 9 | let rows = [ |
| 10 | { id: "queue-layout", text: "检查页面布局和交互", version: "1" }, |
| 11 | { id: "queue-save", text: "修复消息队列保存失败的问题。\n保留原消息的位置、附件和文件引用。\n完成后验证修改后的正文是否被正确执行。", version: "1" }, |
| 12 | { id: "queue-tests", text: "补充验证并整理结果", version: "1" }, |
| 13 | ]; |
| 14 | const snapshot = (path = "") => ({ revision, paused, recovered: false, sessionPath: path, mutationsSupported: true, |
| 15 | items: rows.map((row, i) => ({ id: row.id, preview: row.text, state: "queued", intent: "followup", position: i + 1, byteSize: row.text.length })), |
| 16 | itemsCount: rows.length, bytes: 512, maxItems: 64, maxBytes: 64 * 1024 * 1024, |
| 17 | }); |
| 18 | const receipts = new Map<string, { itemId: string; disposition: string; position: number; paused: boolean }>(); |
| 19 | const enqueue = (text: string, key: string, steer = false) => { |
| 20 | const previous = receipts.get(key); |
| 21 | if (previous) return previous; |
| 22 | const id = `queue-${++revision}`; |
| 23 | if (!steer) rows.push({ id, text, version: String(revision) }); |
| 24 | const receipt = { itemId: id, disposition: steer ? "steer_accepted" : "queued_followup", position: steer ? 0 : rows.length, paused }; |
| 25 | receipts.set(key, receipt); |
| 26 | return receipt; |
| 27 | }; |
| 28 | const command = async (target: InboxTarget, request: InboxQueueRequest): Promise<InboxQueueResult> => { |
| 29 | const row = rows.find(row => row.id === request.itemId); |
| 30 | const result = (outcome: InboxQueueResult["outcome"], reason?: string) => ({ outcome, reason, snapshot: snapshot(target.sessionPath) }); |
| 31 | if (request.kind === "snapshot") return result("unchanged"); |
| 32 | if (request.kind === "enqueue_steer") return { ...result("applied"), receipt: enqueue(request.text || "", request.idempotencyKey || "", true) }; |
| 33 | if (request.kind === "pause") { paused = Boolean(request.paused); revision++; return result("applied"); } |
| 34 | if (!row) return result("unavailable", "item_missing"); |
| 35 | if (request.kind === "read") return { ...result("unchanged"), edit: { id: row.id, text: row.text, contentVersion: row.version, references: row.id === "queue-save" ? ["问题截图.png", "Composer.tsx"] : [] } }; |
| 36 | if (request.kind === "edit") { |
| 37 | if (row.version !== request.contentVersion) return result("conflict", "content_changed"); |
| 38 | row.text = request.text || ""; row.version = String(++revision); |
| 39 | } else if (request.kind === "move") { |
| 40 | if (revision !== request.queueRevision) return result("conflict", "order_changed"); |
| 41 | if (request.beforeItemId && !rows.some(row => row.id === request.beforeItemId)) return result("conflict", "anchor_missing"); |
| 42 | rows = rows.filter(candidate => candidate !== row); |
| 43 | rows.splice(request.beforeItemId ? rows.findIndex(row => row.id === request.beforeItemId) : rows.length, 0, row); revision++; |
| 44 | } else if (request.kind === "delete" || request.kind === "steer") { rows = rows.filter(candidate => candidate !== row); revision++; } |
| 45 | return result("applied"); |
| 46 | }; |
| 47 | return { snapshot, command, enqueue, lookup: (key: string) => receipts.get(key) }; |
| 48 | } |
| 49 | |
| 50 | export function createInboxQueuePreviewBindings() { |
| 51 | const queue = createInboxQueuePreview(); |
| 52 | const bindings: Pick<AppBindings, "CaptureInboxTarget" | "InboxQueueForTarget" | "EnqueueInboxFollowupForTarget" | "LookupInboxFollowupForTarget" | "InboxSnapshot"> = { |
| 53 | CaptureInboxTarget: async (tabId, sessionPath) => ({ tabId, sessionPath, generation: 1, selection: 0, remote: false }), |
| 54 | InboxQueueForTarget: queue.command, |
| 55 | EnqueueInboxFollowupForTarget: async (_target, display, submit, _invocations, key) => queue.enqueue(submit || display, key), |
| 56 | LookupInboxFollowupForTarget: async (_target, key) => { |
| 57 | const receipt = queue.lookup(key); |
| 58 | if (!receipt) throw new Error("Follow-up receipt unconfirmed"); |
| 59 | return receipt; |
| 60 | }, |
| 61 | InboxSnapshot: async () => queue.snapshot(), |
| 62 | }; |
| 63 | return bindings; |
| 64 | } |
| 65 |