# -*- coding: utf-8 -*- """Restore remaining CJK in TopologyCanvas.tsx (context-anchored).""" from pathlib import Path p = Path(__file__).resolve().parents[1] / "src" / "TopologyCanvas.tsx" text = p.read_text(encoding="utf-8") replacements = [ ("// iframe ??????????? Tab ??", "// iframe 内无法全屏时,通知父页 Tab 全屏"), ('{ type: "create_node", text: "???", parentId: parent }', '{ type: "create_node", text: "新节点", parentId: parent }'), (': { type: "create_node", text: "???" },', ': { type: "create_node", text: "新节点" },'), ( ' text: "????",\n parentId: selectedIds[0],\n sidePref: -1,', ' text: "左侧子主题",\n parentId: selectedIds[0],\n sidePref: -1,', ), ( ' text: "????",\n parentId: selectedIds[0],\n sidePref: 1,', ' text: "右侧子主题",\n parentId: selectedIds[0],\n sidePref: 1,', ), ( ' text: "????",\n parentId: parentOfSel,', ' text: "兄弟节点",\n parentId: parentOfSel,', ), ('label: "??",', 'label: "关联",'), ("// Free place ?pin every dragged id", "// Free place → pin every dragged id"), ( ' text: "???",\n ...(selectedIds[0] ? { parentId: selectedIds[0] } : {}),', ' text: "新节点",\n ...(selectedIds[0] ? { parentId: selectedIds[0] } : {}),', ), ('aria-label="????"\n onPointerDown={(e) => e.stopPropagation()}\n >\n {embed ? (', 'aria-label="画布工具"\n onPointerDown={(e) => e.stopPropagation()}\n >\n {embed ? ('), ('title="??????"\n onClick={requestBackToRoster}\n >\n ??\n ', 'title="返回画布列表"\n onClick={requestBackToRoster}\n >\n 返回\n '), ('["select", "??", "V"]', '["select", "选择", "V"]'), ('["pan", "??", "H"]', '["pan", "平移", "H"]'), ('["link", "??", "C"]', '["link", "连线", "C"]'), ( 'title="???????????? (N)??? Shift ??????"\n aria-pressed={dragScope === "node"}\n onClick={() => setDragScope("node")}\n >\n ??\n ', 'title="单点拖拽:只移动当前节点 (N);按住 Shift 也可临时单点"\n aria-pressed={dragScope === "node"}\n onClick={() => setDragScope("node")}\n >\n 单点\n ', ), ( 'title="?????????????? (B)"\n aria-pressed={dragScope === "branch"}\n onClick={() => setDragScope("branch")}\n >\n ??\n ', 'title="整枝拖拽:节点与子孙一起移动 (B)"\n aria-pressed={dragScope === "branch"}\n onClick={() => setDragScope("branch")}\n >\n 整枝\n ', ), ( 'title="?????????????"\n aria-pressed={!cardView}\n disabled={locked}\n onClick={() => {\n if (!cardView) return;\n void setPrefs({ nodeViewMode: "bubble" });\n }}\n >\n ??\n ', 'title="气泡视图:精简标题"\n aria-pressed={!cardView}\n disabled={locked}\n onClick={() => {\n if (!cardView) return;\n void setPrefs({ nodeViewMode: "bubble" });\n }}\n >\n 气泡\n ', ), ( 'title="??????????????"\n aria-pressed={cardView}\n disabled={locked}\n onClick={() => {\n if (cardView) return;\n void setPrefs({ nodeViewMode: "card" });\n }}\n >\n ??\n ', 'title="卡片视图:展开属性面板"\n aria-pressed={cardView}\n disabled={locked}\n onClick={() => {\n if (cardView) return;\n void setPrefs({ nodeViewMode: "card" });\n }}\n >\n 卡片\n ', ), ( 'title="??????????????????"\n disabled={\n locked ||\n !selectedIds[0] ||\n !nodes.find((n) => n.id === selectedIds[0])?.pinned\n }\n onClick={() => {\n if (!selectedIds[0]) return;\n void applyOps([\n { type: "set_pinned", nodeId: selectedIds[0], pinned: false },\n ]);\n }}\n >\n ??\n ', 'title="取消钉住:让选中节点重新参与自动布局"\n disabled={\n locked ||\n !selectedIds[0] ||\n !nodes.find((n) => n.id === selectedIds[0])?.pinned\n }\n onClick={() => {\n if (!selectedIds[0]) return;\n void applyOps([\n { type: "set_pinned", nodeId: selectedIds[0], pinned: false },\n ]);\n }}\n >\n 解钉\n ', ), ( 'title="??"\n disabled={!lastChangeSetId}\n onClick={() => void undo()}\n >\n ??\n ', 'title="撤销"\n disabled={!lastChangeSetId}\n onClick={() => void undo()}\n >\n 撤销\n ', ), ( 'aria-label="????"\n onPointerDown={(e) => e.stopPropagation()}\n >\n e.stopPropagation()}\n >\n