{"record":{"id":"f781a94979bbdef9","repo":"jackwener/OpenCLI","slug":"card-editor-index-1-is-empty-after-typ","errorCode":null,"errorMessage":"文字配图: card editor #${index + 1} is empty after typing","messagePattern":"文字配图: card editor #(.+?) is empty after typing","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/publish.js","lineNumber":904,"sourceCode":"    const focused = await focusActiveCard(page);\n    if (!focused?.ok)\n        throw new CommandExecutionError(`文字配图: could not focus card editor #${index + 1}`);\n    if (typeof page.insertText === 'function') {\n        const lines = text.split('\\n');\n        for (let i = 0; i < lines.length; i++) {\n            if (i > 0 && typeof page.pressKey === 'function')\n                await page.pressKey('Enter');\n            if (lines[i])\n                await page.insertText(lines[i]);\n        }\n    }\n    else {\n        await page.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(text)})`);\n    }\n    await page.wait({ time: 0.4 });\n    const state = await activeCardText(page);\n    if (!state?.ok)\n        throw new CommandExecutionError(`文字配图: card editor #${index + 1} is empty after typing`);\n}\n/**\n * On the 预览图片 step, optionally pick a style. The style picker is a\n * scrollable strip whose options are loaded lazily, so we scroll it to the end\n * to surface every option, then read the real on-page labels (no hard-coded\n * whitelist — XHS adds/removes styles over time). The strip is located by\n * anchoring on a known seed label (e.g. 基础) rather than a volatile class name.\n *\n * If the caller requested a style, it is a write-side postcondition: either\n * that style is available and clicked, or publishing fails before submit.\n */\nasync function selectCardStyle(page, styleName) {\n    if (!styleName || styleName === DEFAULT_CARD_STYLE)\n        return DEFAULT_CARD_STYLE; // default 基础 is preselected — nothing to do.\n    // The style strip (\".cover-list-container\") is VIRTUALIZED: only the items\n    // near the viewport are in the DOM, so a single read sees ~10 of ~20 options\n    // and an option scrolled out of view cannot be clicked. Step-scroll the strip\n    // (\".cover-list-container-wrapper\"), accumulating every label seen; stop as","sourceCodeStart":886,"sourceCodeEnd":922,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/publish.js#L886-L922","documentation":"After typing text into a card editor, fillCard reads back the editor content via activeCardText and throws if the read reports not-ok/empty — i.e. the typed text did not land in the editor.","triggerScenarios":"insertText (or execCommand insertText fallback) silently did nothing: editor lost focus between focus and insert, the editor is not a contenteditable, or the verification read happened before text rendered (0.4s wait insufficient).","commonSituations":"Card content emptied because a XHS overlay/popover stole focus mid-type; multi-line text with page.pressKey unavailable for Enter; XHS replaced its editor implementation so insertText no longer works; card editor re-rendered and cleared input.","solutions":["Re-run the flow; focus loss races are usually transient.","Ensure the page wrapper exposes insertText and pressKey so the primary typing path is used instead of execCommand.","Increase the post-type wait (the code waits 0.4s) by retrying with slower pacing.","If persistent, update activeCardText selectors — XHS DOM likely changed."],"exampleFix":"// before\nawait fillCard(page, \"line1\\nline2\", 0);\n// after\ntry {\n  await fillCard(page, \"line1\\nline2\", 0);\n} catch (e) {\n  if (/is empty after typing/.test(e.message)) {\n    await fillCard(page, \"line1\\nline2\", 0); // retry once after refocus\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"if (typeof page.insertText !== 'function' && typeof page.pressKey !== 'function') console.warn('typing support limited; multi-line cards may fail');","typeGuard":"null","tryCatchPattern":"try { await fillCard(page, text, i); }\ncatch (e) {\n  if (/is empty after typing/.test(e.message)) {\n    await fillCard(page, text, i); // refocus-and-retype once\n  } else throw e;\n}","preventionTips":["Ensure insertText/pressKey are implemented on the page wrapper","Keep card text short enough to type in one pass without overlays","Verify content after typing and retype on empty reads"],"tags":["browser-automation","input-failure","verification"],"backgroundTag":"text-input-not-applied","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}