{"record":{"id":"f97d670e5a87fb2a","repo":"siyuan-note/siyuan","slug":"window-siyuan-languages-clipboardpermissiondenied","errorCode":null,"errorMessage":"window.siyuan.languages.clipboardPermissionDenied","messagePattern":"window\\.siyuan\\.languages\\.clipboardPermissionDenied","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/protyle/util/compatibility.ts","lineNumber":433,"sourceCode":"    const fallbackToPlainText = options.fallbackToPlainText !== false;\n    try {\n        if (isInAndroid()) {\n            if (textSiyuan) {\n                window.JSAndroid.writeSiYuanHTMLClipboard(textPlain, textHTML, textSiyuan);\n                return {status: \"rich\"};\n            }\n            if (textHTML) {\n                window.JSAndroid.writeHTMLClipboard(textPlain, textHTML);\n                return {status: \"rich\"};\n            }\n            window.JSAndroid.writeClipboard(textPlain);\n            return {status: \"plain\"};\n        }\n        if (isInHarmony()) {\n            if (textHTML || textSiyuan) {\n                // 使用通用 HTML 注释封装，使旧版鸿蒙壳也不会写入可见的内部格式分隔符。\n                if (window.JSHarmony.writeHTMLClipboard(textPlain, buildWebClipboardHTML(textHTML, textSiyuan)) === false) {\n                    throw new Error(window.siyuan.languages.clipboardPermissionDenied);\n                }\n                return {status: \"rich\"};\n            }\n            if (window.JSHarmony.writeClipboard(textPlain) === false) {\n                throw new Error(window.siyuan.languages.clipboardPermissionDenied);\n            }\n            return {status: \"plain\"};\n        }\n        if (isInIOS()) {\n            window.webkit.messageHandlers.setClipboard.postMessage(textPlain || textHTML);\n            return {status: \"plain\"};\n        }\n        if (textHTML && navigator.clipboard?.write && typeof ClipboardItem !== \"undefined\") {\n            const clipboardItem: Record<string, Blob> = {};\n            if (textPlain) {\n                clipboardItem[\"text/plain\"] = new Blob([textPlain], {type: \"text/plain\"});\n            }\n            const webHTML = buildWebClipboardHTML(textHTML, textSiyuan);","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/app/src/protyle/util/compatibility.ts#L415-L451","documentation":"writeClipboardData on HarmonyOS delegates to the native shell via window.JSHarmony.writeHTMLClipboard / writeClipboard. When the shell returns false — meaning the OS denied clipboard write permission — the function throws an Error carrying the localized message window.siyuan.languages.clipboardPermissionDenied.","triggerScenarios":"Copying editor content (plain or rich) in the HarmonyOS app while the system clipboard permission is denied or the shell bridge returns false — e.g. user revoked clipboard access, the shell is in a state blocking clipboard writes, or an old Harmony shell lacking writeHTMLClipboard support.","commonSituations":"User denied the clipboard permission prompt in the Harmony app; OS background-clipboard restrictions; using a Harmony shell version without the JSHarmony clipboard bridge methods (bridge undefined / returns false).","solutions":["Grant the SiYuan app clipboard permission in the HarmonyOS system settings and retry the copy","Update the Harmony shell (siyuan-harmony) to a version implementing writeHTMLClipboard/writeClipboard correctly","Catch this error in copy actions and show the localized permission-denied message with a hint to re-copy manually","Verify window.JSHarmony exists before calling it and degrade to a plain-text fallback copy path"],"exampleFix":"// before\nwriteClipboardData(protyle, textPlain, textHTML, textSiyuan); // throws on Harmony if denied\n// after\ntry {\n  await writeClipboardData(protyle, textPlain, textHTML, textSiyuan);\n} catch (e) {\n  if (e.message === window.siyuan.languages.clipboardPermissionDenied) {\n    showMessage(window.siyuan.languages.clipboardPermissionDenied);\n  }\n}","handlingStrategy":"try-catch","validationCode":"const harmonyClipboardAvailable =\n  typeof window.JSHarmony !== \"undefined\" &&\n  typeof window.JSHarmony.writeClipboard === \"function\";","typeGuard":"const hasHarmonyClipboardBridge = (): boolean =>\n  typeof (window as any).JSHarmony !== \"undefined\" &&\n  typeof (window as any).JSHarmony.writeHTMLClipboard === \"function\";","tryCatchPattern":"try {\n  await writeClipboardData(protyle, textPlain, textHTML, textSiyuan);\n} catch (e) {\n  if (e.message === window.siyuan.languages.clipboardPermissionDenied) {\n    showMessage(window.siyuan.languages.clipboardPermissionDenied);\n    return;\n  }\n  throw e;\n}","preventionTips":["Check for the JSHarmony bridge before calling it and provide a web fallback","Prompt users to grant clipboard permission on first copy in the Harmony app","Update the Harmony shell to support writeHTMLClipboard","Guide users to system settings when the error is caught"],"tags":["clipboard","harmony","permission"],"backgroundTag":"permission-denied","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}