{"behavior":"degenerate_repetition","label_filter":null,"limit":10,"next_offset":32600,"offset":32590,"previous_offset":null,"rows":[{"assistant_initiates":false,"asst_text":"\u0645\u0631\u062d\u0628\u064b\u0627! \u0643\u064a\u0641 \u064a\u0645\u0643\u0646\u0646\u064a \u0645\u0633\u0627\u0639\u062f\u062a\u0643 \u0627\u0644\u064a\u0648\u0645\u061f","behavior":"degenerate_repetition","conversation_hash":"0b70b1a8bd52ec9373c2ebfca451d8c2","conversation_redacted":false,"conversation_toxic":false,"country":"Libya","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"Arabic","model":"gpt-4.1-mini-2025-04-14","n_chars":39,"n_turns_in_conversation":5,"n_words":6,"source_row_offset":32590,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 07:44:27 GMT","turn_id":"fe7dc6ee1bcc842cbd312759","turn_index":1,"user_text":"\u0645\u0631\u062d\u0628\u0627"},{"assistant_initiates":false,"asst_text":"\u064a\u0645\u0643\u0646\u0646\u064a \u0645\u0633\u0627\u0639\u062f\u062a\u0643 \u0641\u064a \u0625\u0639\u062f\u0627\u062f \u0645\u0634\u0631\u0648\u0639 \u0628\u0633\u064a\u0637 \u0644\u0644\u0639\u0628\u0629 \u0639\u0644\u0649 Godot 4 \u0644\u0644\u0627\u0646\u062f\u0631\u0648\u064a\u062f \u062a\u0643\u0648\u0646 \u0634\u0628\u064a\u0647\u0629 \u0628\u0644\u0639\u0628\u0629 Specimen Zero\u060c \u0648\u0633\u0623\u0642\u062f\u0645 \u0644\u0643 \u0627\u0644\u0634\u0631\u062d \u0648\u0627\u0644\u062e\u0637\u0648\u0627\u062a \u0644\u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u0645\u0634\u0631\u0648\u0639\u060c \u062b\u0645 \u0623\u0648\u062c\u0647\u0643 \u0644\u0643\u064a\u0641\u064a\u0629 \u062a\u0635\u062f\u064a\u0631\u0647 \u0625\u0644\u0649 \u0645\u0644\u0641 ZIP \u0628\u0646\u0641\u0633\u0643 \u0644\u0623\u0646\u0646\u064a \u0644\u0627 \u0623\u0633\u062a\u0637\u064a\u0639 \u0625\u0646\u0634\u0627\u0621 \u0645\u0644\u0641\u0627\u062a \u0645\u0628\u0627\u0634\u0631\u0629.\n\n### \u0623\u0648\u0644\u0627\u064b: \u0645\u0644\u0627\u062d\u0638\u0627\u062a \u062d\u0648\u0644 \u0644\u0639\u0628\u0629 Specimen Zero\n\u0644\u0639\u0628\u0629 Specimen Zero \u0647\u064a \u0644\u0639\u0628\u0629 \u0628\u0642\u0627\u0621 (Survival) \u0641\u064a \u0628\u064a\u0626\u0629 \u0645\u0638\u0644\u0645\u0629 \u0645\u0639 \u0639\u0646\u0627\u0635\u0631 \u062d\u0631\u0643\u0629 \u0648\u0645\u0647\u0627\u0645. \u0633\u0623\u0633\u0627\u0639\u062f\u0643 \u0628\u0625\u0646\u0634\u0627\u0621 \u0645\u0634\u0631\u0648\u0639 \u064a\u062d\u062a\u0648\u064a \u0639\u0644\u0649 \u0645\u0634\u0627\u0647\u062f \u0623\u0633\u0627\u0633\u064a\u0629\u060c \u062d\u0631\u0643\u0629 \u0634\u062e\u0635\u064a\u0629\u060c \u0648\u0627\u0644\u062a\u062d\u0643\u0645 \u0628\u0627\u0644\u0644\u0645\u0633 (\u0644\u0644\u0627\u0646\u062f\u0631\u0648\u064a\u062f).\n\n---\n\n## \u062e\u0637\u0648\u0627\u062a \u0625\u0646\u0634\u0627\u0621 \u0645\u0634\u0631\u0648\u0639 Godot 4 \u0634\u0628\u064a\u0647 \u0628\u0640 Specimen Zero\n\n### 1. \u0625\u0639\u062f\u0627\u062f \u0627\u0644\u0645\u0634\u0631\u0648\u0639\n- \u0627\u0641\u062a\u062d Godot 4.\n- \u0623\u0646\u0634\u0626 \u0645\u0634\u0631\u0648\u0639 \u062c\u062f\u064a\u062f \u0628\u0627\u0633\u0645 `SpecimenZeroClone`.\n- \u062d\u062f\u062f \u0645\u062c\u0644\u062f \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0641\u064a \u062c\u0647\u0627\u0632\u0643.\n\n### 2. \u0625\u0646\u0634\u0627\u0621 \u0645\u0634\u0647\u062f \u0627\u0644\u0644\u0627\u0639\u0628\n- \u0623\u0646\u0634\u0626 \u0645\u0634\u0647\u062f \u062c\u062f\u064a\u062f (`Player.tscn`) \u064a\u062d\u062a\u0648\u064a \u0639\u0644\u0649 Node \u0645\u0646 \u0627\u0644\u0646\u0648\u0639 `CharacterBody3D`.\n- \u0623\u0636\u0641 \u0644\u0647 MeshInstance3D \u0623\u0648 \u0646\u0645\u0648\u0630\u062c \u0623\u0633\u0627\u0633\u064a (\u0645\u062b\u0644 Capsule) \u0644\u062a\u0645\u062b\u064a\u0644 \u0627\u0644\u0644\u0627\u0639\u0628.\n- \u0623\u0636\u0641 \u0643\u0627\u0645\u064a\u0631\u0627 (Camera3D) \u0643\u0627\u0628\u0646 \u0644\u0644\u0640 Player \u0644\u064a\u0643\u0648\u0646 \u0645\u0646\u0638\u0648\u0631 \u0627\u0644\u0634\u062e\u0635 \u0627\u0644\u062b\u0627\u0644\u062b/\u0627\u0644\u0623\u0648\u0644.\n\n### 3. \u0633\u0643\u0631\u0628\u062a \u062d\u0631\u0643\u0629 \u0627\u0644\u0644\u0627\u0639\u0628 (`Player.gd`)\n\n\u0645\u062b\u0627\u0644 \u0628\u0633\u064a\u0637 \u0644\u062d\u0631\u0643\u0629 3D \u0645\u0639 \u062f\u0639\u0645 \u0644\u0644\u062a\u062d\u0643\u0645 \u0627\u0644\u0644\u0645\u0633\u064a (\u0645\u0641\u0639\u0644 \u0636\u0645\u0646 \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0644\u0644\u0627\u0646\u062f\u0631\u0648\u064a\u062f):\n\n```gdscript\nextends CharacterBody3D\n\n@export var speed := 5.0\n@export var mouse_sensitivity := 0.3\n\nvar velocity = Vector3.ZERO\nvar rotation_x = 0.0\n\nfunc _ready():\n    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)\n\nfunc _process(delta):\n    var input_dir = Vector3.ZERO\n    if Input.is_action_pressed(\"move_forward\"):\n        input_dir.z -= 1\n    if Input.is_action_pressed(\"move_backward\"):\n        input_dir.z += 1\n    if Input.is_action_pressed(\"move_left\"):\n        input_dir.x -= 1\n    if Input.is_action_pressed(\"move_right\"):\n        input_dir.x += 1\n\n    input_dir = input_dir.normalized()\n\n    var direction = (transform.basis * input_dir).normalized()\n\n    velocity.x = direction.x * speed\n    velocity.z = direction.z * speed\n\n    velocity.y += ProjectSettings.get_setting(\"physics/3d/default_gravity\") * delta\n\n    velocity = move_and_slide(velocity, Vector3.UP)\n\nfunc _unhandled_input(event):\n    if event is InputEventMouseMotion:\n        rotate_y(-event.relative.x * mouse_sensitivity * 0.01)\n        rotation_x = clamp(rotation_x - event.relative.y * mouse_sensitivity * 0.01, deg2rad(-80), deg2rad(80))\n        $Camera3D.rotation.x = rotation_x\n```\n\n### 4. \u0625\u0639\u062f\u0627\u062f \u0627\u0644\u062a\u062d\u0643\u0645 (InputMap)\n- \u0641\u064a \u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0627\u0644\u0645\u0634\u0631\u0648\u0639 > Input Map \u0623\u0636\u0641 \u0627\u0644\u062d\u0631\u0643\u0627\u062a:\n  - `move_forward` \u2192 W / Up\n  - `move_backward` \u2192 S / Down\n  - `move_left` \u2192 A / Left\n  - `move_right` \u2192 D / Right\n\n(Godot \u064a\u062d\u0648\u0644 \u062a\u0644\u0642\u0627\u0626\u064a\u0627\u064b \u062d\u062a\u0649 \u062a\u0639\u0645\u0644 \u0647\u0630\u0647 \u0627\u0644\u062d\u0631\u0643\u0627\u062a \u0639\u0644\u0649 \u0627\u0644\u0623\u0646\u062f\u0631\u0648\u064a\u062f \u0628\u0627\u0633\u062a\u062e\u062f\u0627\u0645 \u0627\u0644\u0634\u0627\u0634\u0629 \u0627\u0644\u0644\u0645\u0633\u064a\u0629 \u0645\u0639 \u0625\u0636\u0627\u0641\u0629 \u0648\u0627\u062c\u0647\u0629 \u062a\u062d\u0643\u0645 - \u0644\u0645\u0632\u064a\u062f \u0645\u0646 \u0627\u0644\u062a\u062e\u0635\u064a\u0635 \u0641\u064a \u062d\u0627\u0644\u0629 \u0627\u0644\u0644\u0645\u0633 \u064a\u0645\u0643\u0646\u0643 \u0625\u0636\u0627\u0641\u0629 \u0623\u0632\u0631\u0627\u0631 \u0639\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629)\n\n---\n\n### 5. \u0627\u0644\u0645\u0634\u0627\u0647\u062f \u0627\u0644\u0625\u0636\u0627\u0641\u064a\u0629\n\n- \u0645\u0634\u0647\u062f \u0627\u0644\u0628\u064a\u0626\u0629 (Simple ground plane).\n- \u0627\u0636\u0641 \u0628\u0639\u0636 \u0627\u0644\u062e\u0635\u0648\u0645 \u0623\u0648 \u0627\u0644\u0639\u0642\u0628\u0627\u062a \u062d\u0633\u0628 \u0627\u0644\u0645\u0637\u0644\u0648\u0628.\n\n---\n\n### 6. \u062a\u0635\u062f\u064a\u0631 \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0643\u0640 APK \u0644\u0644\u0623\u0646\u062f\u0631\u0648\u064a\u062f\n\n- \u0642\u0645 \u0628\u062a\u062b\u0628\u064a\u062a Android SDK & NDK \u0648\u062a\u0647\u064a\u0626\u0629 Godot \u0644\u0644\u0639\u0645\u0644 \u0645\u0639 \u0623\u0646\u062f\u0631\u0648\u064a\u062f.\n- \u0627\u0646\u0634\u0626 \u0645\u0644\u0641 export template (\u0625\u0646 \u0644\u0645 \u064a\u0643\u0646 \u0645\u0648\u062c\u0648\u062f).\n- \u0627\u0630\u0647\u0628 \u0625\u0644\u0649 Project > Export > Add Android.\n- \u0639\u062f\u0644 \u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a \u0645\u062b\u0644 \u0627\u0633\u0645 \u0627\u0644\u062d\u0632\u0645\u0629\u060c \u0627\u0644\u0631\u0645\u0632...\n- Export to APK.\n\n---\n\n### 7. \u062d\u0641\u0638 \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0639\u0644\u0649 \u0634\u0643\u0644 \u0645\u0644\u0641 ZIP\n\n- \u0628\u0639\u062f \u0627\u0644\u0627\u0646\u062a\u0647\u0627\u0621 \u0645\u0646 \u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u0645\u0634\u0631\u0648\u0639\u060c \u062a\u0648\u062c\u0647 \u0625\u0644\u0649 \u0645\u062c\u0644\u062f \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0639\u0644\u0649 \u062c\u0647\u0627\u0632\u0643.\n- \u0642\u0645 \u0628\u062a\u062d\u062f\u064a\u062f \u0643\u0627\u0641\u0629 \u0627\u0644\u0645\u0644\u0641\u0627\u062a \u0648\u0627\u0644\u0645\u062c\u0644\u062f\u0627\u062a.\n- \u0627\u0636\u063a\u0637 \u0643\u0644\u064a\u0643 \u064a\u0645\u064a\u0646 \u2192 \u201cSend to\u201d \u2192 \u201cCompressed (zipped) folder\u201d (\u0639\u0644\u0649 \u0648\u064a\u0646\u062f\u0648\u0632) \u0623\u0648 \u0627\u0633\u062a\u062e\u062f\u0645 \u0623\u064a \u0623\u062f\u0627\u0629 \u0636\u063a\u0637.\n\n---\n\n## \u0647\u0644 \u062a\u0648\u062f \u0623\u0646 \u0623\u0643\u062a\u0628\u0647 \u0644\u0643 \u0641\u064a \u0634\u0643\u0644 \u0645\u0634\u0631\u0648\u0639 \u062c\u0627\u0647\u0632 \u0644\u0644\u0646\u0633\u062e\u061f\n\n\u064a\u0645\u0643\u0646\u0646\u064a \u062a\u0632\u0648\u064a\u062f\u0643 \u0628\u0645\u062c\u0644\u062f \u0645\u0634\u0627\u0631\u064a\u0639 \u0628\u0633\u064a\u0637 \u0639\u0644\u0649 \u0634\u0643\u0644 \u0646\u0635 \u0623\u0648 \u0631\u0648\u0627\u0628\u0637 \u0627\u0644\u062a\u0639\u0644\u064a\u0645\u0627\u062a \u0627\u0644\u0628\u0631\u0645\u062c\u064a\u0629 \u0645\u0641\u0635\u0644\u0629 \u0644\u062a\u0642\u0648\u0645 \u0628\u0646\u0633\u062e\u0647\u0627 \u0625\u0644\u0649 \u0645\u0634\u0631\u0648\u0639\u0643 :)\n\n---\n\n\u0647\u0644 \u062a\u0648\u062f \u0623\u0646 \u0623\u0632\u0648\u062f\u0643 \u0628\u0627\u0644\u0645\u062c\u0644\u062f\u0627\u062a/\u0627\u0644\u0645\u0644\u0641\u0627\u062a \u0627\u0644\u0623\u0633\u0627\u0633\u064a\u0629 \u0639\u0644\u0649 \u0634\u0643\u0644 \u0646\u0635\u0648\u0635 \u0644\u0646\u0633\u062e\u0647\u0627\u061f \u0648\u0647\u0644 \u062a\u0631\u064a\u062f \u062a\u0639\u0644\u064a\u0645\u0627\u062a \u0623\u0643\u062b\u0631 \u062a\u0641\u0635\u064a\u0644\u0627\u064b \u0644\u0625\u0646\u0634\u0627\u0621 \u0627\u0644\u062a\u062d\u0643\u0645 \u0628\u0627\u0644\u0644\u0645\u0633 \u0623\u0648 \u0627\u0644\u0628\u064a\u0626\u0629\u061f","behavior":"degenerate_repetition","conversation_hash":"0b70b1a8bd52ec9373c2ebfca451d8c2","conversation_redacted":false,"conversation_toxic":false,"country":"Libya","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"Arabic","model":"gpt-4.1-mini-2025-04-14","n_chars":3447,"n_turns_in_conversation":5,"n_words":506,"source_row_offset":32591,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 07:44:27 GMT","turn_id":"47bb32eede4841714110bd4b","turn_index":2,"user_text":"\u0627\u0631\u064a\u062f\u0643 \u0627\u0646 \u062a\u0648\u0644\u062f \u0645\u0644\u0641 zip \u0644\u0645\u0634\u0631\u0648\u0639 \u0644\u0639\u0628\u0629 godot 4 android  \u0634\u0628\u064a\u0647\u0629 \u0644\u0644\u0639\u0628\u0629  \u0644 Specimen Zero "},{"assistant_initiates":false,"asst_text":"\u0643\u0645\u0627 \u0630\u0643\u0631\u062a \u0633\u0627\u0628\u0642\u064b\u0627\u060c \u0644\u0627 \u0623\u0633\u062a\u0637\u064a\u0639 \u0625\u0646\u0634\u0627\u0621 \u0645\u0644\u0641 ZIP \u0645\u0628\u0627\u0634\u0631 \u0623\u0648 \u0631\u0641\u0639 \u0645\u0644\u0641\u0627\u062a \u0647\u0646\u0627. \u0644\u0643\u0646 \u0628\u0625\u0645\u0643\u0627\u0646\u064a \u0645\u0633\u0627\u0639\u062f\u062a\u0643 \u0639\u0644\u0649 \u0625\u0646\u0634\u0627\u0621 \u0645\u0634\u0631\u0648\u0639 Godot 4 \u0643\u0627\u0645\u0644 \u062e\u0637\u0648\u0629 \u0628\u062e\u0637\u0648\u0629\u060c \u0648\u0625\u0639\u0637\u0627\u0624\u0643 \u0645\u0644\u0641\u0627\u062a \u0627\u0644\u0645\u0634\u0631\u0648\u0639 (\u0645\u0634\u0627\u0647\u062f\u060c \u0633\u0643\u0631\u0628\u062a\u0627\u062a\u060c \u0625\u0639\u062f\u0627\u062f\u0627\u062a) \u0628\u062a\u0646\u0633\u064a\u0642 \u0646\u0635 \u064a\u0645\u0643\u0646\u0643 \u0646\u0633\u062e\u0647 \u0648\u0644\u0635\u0642\u0647 \u0641\u064a \u0645\u0644\u0641 \u0639\u0644\u0649 \u062c\u0647\u0627\u0632\u0643\u060c \u062b\u0645 \u062a\u0636\u063a\u0637 \u0639\u0644\u0649 \u0627\u0644\u0645\u062c\u0644\u062f \u0628\u0646\u0641\u0633\u0643 \u0644\u064a\u0635\u0628\u062d ZIP.\n\n\u0647\u0644 \u062a\u0648\u062f \u0623\u0646 \u0623\u0628\u062f\u0623 \u0628\u0625\u0646\u0634\u0627\u0621 \u0645\u0634\u0631\u0648\u0639 \u0628\u0633\u064a\u0637 \u0645\u0646 \u0627\u0644\u0635\u0641\u0631 \u0644\u0643 \u0648\u0623\u0631\u0633\u0644 \u0644\u0643 \u0627\u0644\u0645\u0644\u0641\u0627\u062a (\u0645\u062b\u0644 `project.godot`\u060c \u0645\u0634\u0627\u0647\u062f `.tscn`\u060c \u0633\u0643\u0631\u0628\u062a\u0627\u062a `.gd`) \u0643\u0645\u0644\u0641\u0627\u062a \u0646\u0635\u064a\u0629 \u064a\u0645\u0643\u0646\u0643 \u0646\u0633\u062e\u0647\u0627\u061f\n\n\u0625\u0630\u0627 \u0646\u0639\u0645\u060c \u0641\u0642\u0637 \u0623\u062e\u0628\u0631\u0646\u064a\u060c \u0648\u0625\u0630\u0627 \u0623\u0631\u062f\u062a \u064a\u0645\u0643\u0646\u0646\u064a \u0623\u064a\u0636\u064b\u0627 \u0623\u0646 \u0623\u0648\u0636\u062d \u0644\u0643 \u0643\u064a\u0641 \u062a\u0642\u0648\u0645 \u0628\u0636\u063a\u0637 \u0627\u0644\u0645\u062c\u0644\u062f \u0625\u0644\u0649 \u0645\u0644\u0641 zip \u0639\u0644\u0649 \u062c\u0647\u0627\u0632\u0643.\n\n---\n\n**\u0645\u0644\u062e\u0635:**\n\n- \u0623\u0631\u0633\u0644 \u0644\u0643 \u0645\u0644\u0641\u0627\u062a \u0627\u0644\u0645\u0634\u0631\u0648\u0639 \u0646\u0635\u064a\u0627\u064b.\n- \u062a\u0646\u0634\u0626 \u0645\u062c\u0644\u062f \u062c\u062f\u064a\u062f \u0641\u064a \u062c\u0647\u0627\u0632\u0643.\n- \u062a\u0646\u0633\u062e \u0627\u0644\u0645\u0644\u0641\u0627\u062a \u0627\u0644\u0646\u0635\u064a\u0629 \u062f\u0627\u062e\u0644 \u0627\u0644\u0645\u062c\u0644\u062f.\n- \u062a\u0633\u062a\u062e\u062f\u0645 \u0623\u062f\u0627\u0629 \u0627\u0644\u0636\u063a\u0637 \u0641\u064a \u062c\u0647\u0627\u0632\u0643 \u0644\u062a\u0643\u0648\u064a\u0646 \u0645\u0644\u0641 ZIP \u0644\u0644\u0645\u0634\u0631\u0648\u0639.\n\n\u0647\u0644 \u062a\u0648\u062f \u0630\u0644\u0643\u061f","behavior":"degenerate_repetition","conversation_hash":"0b70b1a8bd52ec9373c2ebfca451d8c2","conversation_redacted":false,"conversation_toxic":false,"country":"Libya","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"Arabic","model":"gpt-4.1-mini-2025-04-14","n_chars":683,"n_turns_in_conversation":5,"n_words":125,"source_row_offset":32592,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 07:44:27 GMT","turn_id":"10184c6b93354434f87450fa","turn_index":3,"user_text":"\u0627\u0631\u064a\u062f \u0645\u0644\u0641 zip"},{"assistant_initiates":false,"asst_text":"\u062d\u062a\u0649 \u062a\u0627\u0631\u064a\u062e \u0645\u0639\u0631\u0641\u062a\u064a (\u064a\u0648\u0646\u064a\u0648 2024)\u060c \u0644\u0627 \u062a\u0648\u062c\u062f \u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0631\u0633\u0645\u064a\u0629 \u0623\u0648 \u0645\u0648\u062b\u0648\u0642\u0629 \u0639\u0646 \u0646\u0633\u062e\u0629 \u062a\u0633\u0645\u0649 \"GPT-4.1 mini\"\u060c \u0644\u0630\u0627 \u0645\u0646 \u0627\u0644\u0635\u0639\u0628 \u0627\u0644\u0645\u0642\u0627\u0631\u0646\u0629 \u062a\u062d\u062f\u064a\u062f\u064b\u0627 \u0628\u064a\u0646\u0647\u0627 \u0648\u0628\u064a\u0646 \"GPT-4\" (\u0623\u0648 \u0645\u0627 \u062a\u0634\u064a\u0631 \u0625\u0644\u064a\u0647 \u0628\u0640 \"o4\").\n\n\u0644\u0643\u0646 \u0628\u0634\u0643\u0644 \u0639\u0627\u0645:\n\n- **GPT-4** \u0647\u0648 \u0646\u0645\u0648\u0630\u062c \u0643\u0628\u064a\u0631 \u0648\u0645\u062a\u0642\u062f\u0645 \u062c\u062f\u064b\u0627 \u0645\u0646 \u0633\u0644\u0633\u0644\u0629 GPT\u060c \u0648\u064a\u0645\u062a\u0627\u0632 \u0628\u0641\u0647\u0645 \u0623\u0639\u0645\u0642\u060c \u0627\u0633\u062a\u062c\u0627\u0628\u0627\u062a \u0623\u062f\u0642\u060c \u0648\u0642\u062f\u0631\u0629 \u0639\u0644\u0649 \u0627\u0644\u062a\u0639\u0627\u0645\u0644 \u0645\u0639 \u0645\u0647\u0627\u0645 \u0645\u0639\u0642\u062f\u0629.\n- \u0625\u0630\u0627 \"GPT-4.1 mini\" \u0645\u0648\u062c\u0648\u062f\u060c \u0641\u0645\u0646 \u0627\u0644\u0645\u062d\u062a\u0645\u0644 \u0623\u0646\u0647 \u0625\u0635\u062f\u0627\u0631 \u0645\u0635\u063a\u0631 \u0623\u0648 \u0645\u062e\u0641\u0641 \u0645\u0646 GPT-4\u060c \u0645\u0648\u062c\u0647 \u0644\u0623\u062f\u0627\u0621 \u0623\u0633\u0631\u0639 \u0623\u0648 \u0627\u0633\u062a\u0647\u0644\u0627\u0643 \u0623\u0642\u0644 \u0644\u0644\u0645\u0648\u0627\u0631\u062f\u060c \u0648\u0628\u0627\u0644\u062a\u0627\u0644\u064a \u0642\u062f \u064a\u0643\u0648\u0646 \u0623\u0642\u0644 \u0630\u0643\u0627\u0621\u064b \u0623\u0648 \u0642\u062f\u0631\u0629 \u0645\u0642\u0627\u0631\u0646\u0629 \u0627\u0644\u0646\u0633\u062e\u0629 \u0627\u0644\u0643\u0627\u0645\u0644\u0629 GPT-4.\n\n\u0644\u0630\u0627\u060c \u0628\u0634\u0643\u0644 \u0639\u0627\u0645:\n\n> **GPT-4 \u0643\u0627\u0645\u0644 (o4)** \u0647\u0648 \u0623\u0643\u062b\u0631 \u0630\u0643\u0627\u0621\u064b \u0648\u0642\u062f\u0631\u0629 \u0645\u0642\u0627\u0631\u0646\u0629 \u0628\u0625\u0635\u062f\u0627\u0631\u0627\u062a \u0645\u0635\u063a\u0631\u0629 \u0623\u0648 \"mini\"\u060c \u0627\u0644\u062a\u064a \u0642\u062f \u062a\u0643\u0648\u0646 \u0645\u062e\u0635\u0635\u0629 \u0644\u0623\u063a\u0631\u0627\u0636 \u0645\u062d\u062f\u0648\u062f\u0629 \u0623\u0648 \u0644\u062a\u0642\u0644\u064a\u0644 \u062a\u0643\u0644\u0641\u0629 \u0627\u0644\u0645\u0639\u0627\u0644\u062c\u0629.\n\n---\n\n\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0634\u064a\u0631 \u0625\u0644\u0649 \u0634\u064a\u0621 \u0645\u062d\u062f\u062f \u0623\u0648 \u0645\u0646\u0635\u0629 \u0645\u0639\u064a\u0646\u0629 \u062a\u0633\u062a\u062e\u062f\u0645 \u0647\u0630\u0647 \u0627\u0644\u0645\u0635\u0637\u0644\u062d\u0627\u062a\u060c \u0645\u0646 \u0641\u0636\u0644\u0643 \u0642\u062f\u0651\u0645 \u0644\u064a \u0645\u0632\u064a\u062f\u064b\u0627 \u0645\u0646 \u0627\u0644\u062a\u0641\u0627\u0635\u064a\u0644 \u0644\u0623\u062a\u0645\u0643\u0646 \u0645\u0646 \u0625\u0639\u0637\u0627\u0626\u0643 \u062c\u0648\u0627\u0628 \u0623\u062f\u0642.","behavior":"degenerate_repetition","conversation_hash":"0b70b1a8bd52ec9373c2ebfca451d8c2","conversation_redacted":false,"conversation_toxic":false,"country":"Libya","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"Arabic","model":"gpt-4.1-mini-2025-04-14","n_chars":787,"n_turns_in_conversation":5,"n_words":144,"source_row_offset":32593,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 07:44:27 GMT","turn_id":"428fa70d8251d9aa09103aa9","turn_index":4,"user_text":"\u0627\u064a\u0647\u0645 \u0627\u0630\u0643\u0649 gpt 4.1 mini \u0627\u0648 o4"},{"assistant_initiates":false,"asst_text":"L'application Korn Ferry est g\u00e9n\u00e9ralement utilis\u00e9e pour la gestion des talents, le d\u00e9veloppement des comp\u00e9tences, l'\u00e9valuation des employ\u00e9s et la planification de la succession au sein des entreprises. Korn Ferry est une soci\u00e9t\u00e9 sp\u00e9cialis\u00e9e dans le conseil en ressources humaines, notamment en recrutement, leadership, gestion des talents et d\u00e9veloppement organisationnel.\n\nPlus pr\u00e9cis\u00e9ment, l'application Korn Ferry peut permettre de :\n\n- R\u00e9aliser des \u00e9valuations de comp\u00e9tences et de personnalit\u00e9.\n- Identifier les potentiels et les besoins en formation des collaborateurs.\n- Suivre les parcours de carri\u00e8re et planifier la succession.\n- Faciliter le d\u00e9veloppement du leadership.\n- G\u00e9rer les processus de recrutement et de gestion des performances.\n\nSelon le contexte et la version de l'application, elle peut \u00eatre utilis\u00e9e par les RH, les managers ou les employ\u00e9s eux-m\u00eames pour acc\u00e9der \u00e0 des outils de d\u00e9veloppement professionnel.\n\nSi vous avez une version ou un usage sp\u00e9cifique en t\u00eate, n\u2019h\u00e9sitez pas \u00e0 me le pr\u00e9ciser pour que je puisse vous donner une r\u00e9ponse plus pr\u00e9cise !","behavior":"degenerate_repetition","conversation_hash":"dc0ce0489b3ac25abc1e8101b7f19b60","conversation_redacted":false,"conversation_toxic":false,"country":"DR Congo","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"French","model":"gpt-4.1-mini-2025-04-14","n_chars":1118,"n_turns_in_conversation":1,"n_words":169,"source_row_offset":32594,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 07:44:28 GMT","turn_id":"cc8234f6d370ac0c5781ebbc","turn_index":0,"user_text":"\u00c0 quoi sert l'application korn ferry"},{"assistant_initiates":false,"asst_text":"Hello! How can I assist you today?","behavior":"degenerate_repetition","conversation_hash":"f5e8e5fdb83e14ad941775cfa7add6f0","conversation_redacted":false,"conversation_toxic":false,"country":"Nigeria","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"Maori","model":"gpt-4.1-mini-2025-04-14","n_chars":37,"n_turns_in_conversation":1,"n_words":8,"source_row_offset":32595,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 07:47:44 GMT","turn_id":"939aaaf8f64e54ded06dcd4d","turn_index":0,"user_text":"Hi"},{"assistant_initiates":false,"asst_text":"\u0412\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u043c\u043e\u0449\u043d\u044b\u0439 \u0438 \u043c\u043d\u043e\u0433\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438\u0437 Excel-\u0444\u0430\u0439\u043b\u043e\u0432 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438. \u041d\u0438\u0436\u0435 \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u043d \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043e\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c, \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043d\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u0434\u0435:\n\n---\n\n### \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:\n\n1. **\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e Excel \u0444\u0430\u0439\u043b\u0430 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432**  \n   - \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u0432 `.xlsx`, `.xls`  \n   - \u0412\u044b\u0431\u043e\u0440 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 (\u043f\u043e \u043d\u043e\u043c\u0435\u0440\u0443 \u0441\u0442\u043e\u043b\u0431\u0446\u0430)\n\n2. **\u0410\u043d\u0430\u043b\u0438\u0437 \u0438 \u043f\u0430\u0440\u0441\u0438\u043d\u0433 HTML \u0442\u0430\u0431\u043b\u0438\u0446 \u0432 \u044f\u0447\u0435\u0439\u043a\u0430\u0445**  \n   - \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u043c\u043d\u043e\u0433\u043e\u043a\u043e\u043b\u043e\u043d\u043e\u0447\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b HTML  \n   - \u0418\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0431\u0435\u0437 \u0440\u0430\u043d\u043d\u0435\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n\n3. **\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438 \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432**  \n   - \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432  \n   - \u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043c\u0443\u0441\u043e\u0440\u043d\u044b\u0445 \u0438 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439  \n   - \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0440\u0443\u0447\u043d\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430  \n   - \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 (\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c) \u2014 \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u201cB/W\u201d, \u201cI/O\u201d \u0438 \u0434\u0440.\n\n4. **\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 (\u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0435) \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 (\u201c\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\u201d) \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445, \u0432\u043a\u043b\u044e\u0447\u0430\u044f:**  \n   - \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439  \n   - \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0445 \u0437\u0430\u043c\u0435\u043d\u044b (\u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f)  \n   - \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 (\u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430\u0445) \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432  \n   - \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0445 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\n5. **\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 (callbacks)**  \n   - \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435/\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0441 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u044b\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432  \n   - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0437\u0430\u043c\u0435\u043d\u0430 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u043d\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435\n\n6. **\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u0437\u0430\u043c\u0435\u043d\u044b \u0442\u0435\u043a\u0441\u0442\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439**  \n   - \u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043f\u0440\u0430\u0432\u0438\u043b \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0438\u043c\u043f\u043e\u0440\u0442\u0430/\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430  \n   - \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u0440\u043e\u043a\u0430\u0445  \n   - \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435/\u0434\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b  \n   - \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u0438\u0437 JSON\n\n7. **\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430 \u0441\u0442\u0440\u043e\u043a**  \n   - \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430 \u0434\u043b\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439  \n   - \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430 \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n\n8. **\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 (\u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c\u0430\u044f \u043c\u0435\u0436\u0434\u0443 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439, \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0438 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439)**  \n   - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u0433\u043e \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439  \n   - \u0417\u0430\u0449\u0438\u0442\u0430 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440 (H.264, G.711, IP67 \u0438 \u0434\u0440.) \u043e\u0442 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430  \n   - \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 (16 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439)  \n   - \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043d\u0435\u0447\u0435\u0442\u043a\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u0438 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f  \n   - \u0418\u043c\u043f\u043e\u0440\u0442/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439 \u0438 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430  \n   - GUI \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 \u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430  \n   - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0434\u0430\u043d\u043d\u044b\u0445  \n   - \u0421\u0447\u0451\u0442\u0447\u0438\u043a \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438  \n\n9. **\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u043d\u0430\u043b\u0438\u0437 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f, \u0432\u043a\u043b\u044e\u0447\u0430\u044f IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435**  \n   - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u0445 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432 \u0434\u0430\u043d\u043d\u044b\u0445  \n   - \u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u043e\u0439 \u0435\u0434\u0438\u043d\u0438\u0446 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f  \n   - \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0435\u0434\u0438\u043d\u0438\u0446  \n   - \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438\n\n10. **\u0426\u0432\u0435\u0442\u043e\u0432\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0438 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439**  \n    - \u041d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u201c\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\u201d  \n    - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a \u0438 \u0441\u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (\u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439)  \n    - \u0426\u0432\u0435\u0442\u043e\u0432\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435  \n    - \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u0430\u043c\u0438: \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438\u0437 \u0433\u0440\u0443\u043f\u043f, \u043f\u043e\u043b\u043d\u043e\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b  \n    - \u0412\u044b\u0431\u043e\u0440 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f  \n    - \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0434\u043b\u044f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0438\u0445 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\n\n11. **\u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439**  \n    - \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435/\u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b  \n    - \u0423\u0434\u043e\u0431\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0441 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f\u043c\u0438  \n    - \u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 (\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c/\u0434\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c)  \n    - \u0418\u043c\u043f\u043e\u0440\u0442/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0432 JSON \u0444\u043e\u0440\u043c\u0430\u0442\n\n12. **\u041a\u043e\u043c\u0444\u043e\u0440\u0442\u043d\u044b\u0439 \u0438 \u044d\u0440\u0433\u043e\u043d\u043e\u043c\u0438\u0447\u043d\u044b\u0439 GUI**  \n    - \u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u044e\u0449\u0438\u0445 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043e\u043a (ToolTips) \u0434\u043b\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430  \n    - \u041f\u0435\u0440\u0435\u043d\u043e\u0441 \u0440\u0435\u0434\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0432 \u043c\u0435\u043d\u044e  \n    - \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439  \n    - \u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 \u0434\u043b\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439  \n    - \u0412\u043a\u043b\u0430\u0434\u043a\u0438 \u0441 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0443: \u043e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430, \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432, \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0434\u0440.  \n    - \u041e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0438\u0430\u043b\u043e\u0433\u0438 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\u043c\u0438, \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430, \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 \u0437\u0430\u043c\u0435\u043d\u044b \u0438 \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438  \n    - \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u0440\u0430\u0432\u0438\u043b, \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b \u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432  \n    - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438\n\n13. **\u0418\u043c\u043f\u043e\u0440\u0442/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 m\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432**  \n    - \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430/\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438\u0437 JSON  \n    - \u0411\u044b\u0441\u0442\u0440\u0430\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u0438 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439  \n    - \u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\n\n14. **\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438**  \n    - \u0423\u043c\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439  \n    - \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u043e\u0441\u043b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f  \n    - \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439  \n    - \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 (\u043f\u0440\u043e\u0431\u0435\u043b\u044b, \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u044b, \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f)  \n    - \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n\n15. **\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0430\u044f \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0435\u0439 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c**  \n    - \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u0441 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0435\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f  \n    - \u0411\u044b\u0441\u0442\u0440\u0430\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f \u043f\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430\u043c  \n    - \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0438 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f  \n    - \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0433\u043e\u0440\u044f\u0447\u0438\u0445 \u043a\u043b\u0430\u0432\u0438\u0448\u0430\u0445 \u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f\u0445\n\n16. **\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0430**  \n    - \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043b\u043e\u0433-\u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430  \n    - \u0414\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043d\u044b\u0445 \u0442\u043e\u0432\u0430\u0440\u043e\u0432  \n    - \u0412\u044b\u0432\u043e\u0434 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0432\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0445 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\n\n17. **\u0417\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c**  \n    - \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 \u0444\u0430\u0439\u043b\u0430\u043c\u0438, JSON, \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c\u0438 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438  \n    - \u041f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b  \n    - \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0437\u0430\u043c\u0435\u043d\u0430 \u043f\u0443\u0441\u0442\u044b\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 \u043d\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435  \n    - \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0445 \u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0435 HTML \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0438 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435\n\n---\n\n### \u041a\u0440\u0430\u0442\u043a\u043e \u043f\u043e \u043f\u0443\u043d\u043a\u0442\u0430\u043c (\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0447\u0435\u043d\u044c):\n\n- \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 Excel \u0444\u0430\u0439\u043b\u0430 (\u0441 \u0432\u044b\u0431\u043e\u0440\u043e\u043c \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432)  \n- \u041f\u0430\u0440\u0441\u0438\u043d\u0433 HTML \u0432 \u044f\u0447\u0435\u0439\u043a\u0430\u0445 \u0441 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u043c\u043d\u043e\u0433\u043e\u043a\u043e\u043b\u043e\u043d\u043e\u0447\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b  \n- \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0438 \u0440\u0443\u0447\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439  \n- \u0417\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044e\u0442\u0441\u044f \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u043d\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f  \n- \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 \u0438 \u043c\u0443\u0441\u043e\u0440\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432  \n- \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439  \n- \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0437\u0430\u043c\u0435\u043d\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c \u0441 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u043c\u0438 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438  \n- GUI \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 \u0437\u0430\u043c\u0435\u043d\u044b \u0441 \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u043c/\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u043e\u043c  \n- \u041c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 callback-\u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f\u043c\u0438 \u0438 UI \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f  \n- \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438 \u0440\u0443\u0447\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u0432 (\u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f, \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0438\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430)  \n- \u0418\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u043e\u0438\u0441\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u2014 \u0437\u0430\u0449\u0438\u0442\u0430 \u043e\u0442 \u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439  \n- \u0413\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0430 \u0438 \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432  \n- \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432  \n- \u0425\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0438 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u043f\u0440\u0430\u0432\u0438\u043b, \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432, \u0437\u0430\u0449\u0438\u0449\u0451\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u0438 \u043f\u0440.  \n- \u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (\u0432\u044b\u0431\u043e\u0440 \u0432\u0441\u0451/\u0441\u043d\u044f\u0442\u044c \u0432\u044b\u0431\u043e\u0440), \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f, \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0445  \n- \u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0435 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u0440\u0438 \u043b\u044e\u0431\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0445, \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\u0445, \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u0445 \u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\u0445  \n- \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0431\u0430\u0437\u044b \u0435\u0434\u0438\u043d\u0438\u0446 \u0434\u043b\u044f IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f \u0441 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0435\u0439 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432  \n- \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u043d\u0430\u043b\u0438\u0437 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f  \n- \u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u043e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0434\u043b\u044f \u0434\u0438\u0430\u0433\u043d\u043e\u0441\u0442\u0438\u043a\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c  \n- \u0423\u0434\u043e\u0431\u043d\u044b\u0435 \u0434\u0438\u0430\u043b\u043e\u0433\u0438 \u0438 \u0441\u043f\u0440\u0430\u0432\u043a\u0430 \u0441 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0435\u0439 \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c  \n- \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u044b\u0435 \u043c\u0435\u043d\u044e \u0438 \u0433\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f  \n\n---\n\n\u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u044b \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u044f \u043f\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043c\u043e\u0434\u0443\u043b\u044f\u043c/\u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c \u2014 \u043c\u043e\u0433\u0443 \u0434\u0435\u0442\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c!","behavior":"degenerate_repetition","conversation_hash":"208e908a1815d40ec9b124e09c520073","conversation_redacted":false,"conversation_toxic":false,"country":"Russia","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"English","model":"gpt-4.1-mini-2025-04-14","n_chars":368084,"n_turns_in_conversation":1,"n_words":27201,"source_row_offset":32596,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 08:02:01 GMT","turn_id":"0471e5c4859fa3fabb062e2c","turn_index":0,"user_text":"\u0412\u043e\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u043e\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:\nimport re\nimport json\nfrom tkinter import *\nfrom tkinter import ttk, filedialog, messagebox, simpledialog\nfrom collections import defaultdict\nimport os\nimport sys\n\n# ===== \ud83d\udee1\ufe0f \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0415 \u0410\u0411\u0411\u0420\u0415\u0412\u0418\u0410\u0422\u0423\u0420\u042b v4.8 =====\nPROTECTED_ABBREVIATIONS = {\n    # \u0424\u043e\u0440\u043c\u0430\u0442\u044b \u0441\u0436\u0430\u0442\u0438\u044f \u0432\u0438\u0434\u0435\u043e/\u0430\u0443\u0434\u0438\u043e\n    'H.264', 'H.265', 'H.263', 'H.261', 'HEVC', 'AVC', 'MJPEG', 'MPEG-4', 'MPEG-2',\n    'G.711', 'G.722', 'G.726', 'G.729', 'AAC', 'MP3', 'PCM', 'ADPCM',\n    \n    # \u0421\u0442\u0435\u043f\u0435\u043d\u0438 \u0437\u0430\u0449\u0438\u0442\u044b  \n    'IP65', 'IP66', 'IP67', 'IP68', 'IP54', 'IP44', 'IK08', 'IK10',\n    \n    # \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u044b \u0438 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b\n    'TCP/IP', 'HTTP', 'HTTPS', 'FTP', 'SMTP', 'DNS', 'DHCP', 'NTP',\n    'IEEE', 'USB', 'HDMI', 'VGA', 'DVI', 'PoE', 'PoE+', 'UPS',\n    'Wi-Fi', 'Bluetooth', 'Zigbee', 'Z-Wave', 'LoRa', 'NFC', 'RFID',\n    \n    # \u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u044b\n    'LED', 'LCD', 'OLED', 'TFT', 'CRT', 'IR', 'UV', 'RGB', 'CMYK',\n    'CPU', 'GPU', 'RAM', 'ROM', 'SSD', 'HDD', 'API', 'SDK', 'IDE',\n    'ISO', 'IEC', 'FCC', 'CE', 'RoHS', 'UL', 'CSA', 'ETL'\n}\n\n# \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0443\u0442\u044c \u043a \u043c\u043e\u0434\u0443\u043b\u044f\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\ntry:\n    current_dir = os.path.dirname(os.path.abspath(__file__))\nexcept NameError:\n    # Fallback \u0434\u043b\u044f \u0441\u043b\u0443\u0447\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 exec\n    current_dir = os.getcwd()\n\ncode_dir = os.path.join(current_dir, 'code')\nif code_dir not in sys.path:\n    sys.path.insert(0, code_dir)\n\n# \u041e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u044b\ntry:\n    import pandas as pd\n    PANDAS_AVAILABLE = True\nexcept ImportError:\n    print(\"\u26a0\ufe0f pandas \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d - \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\")\n    PANDAS_AVAILABLE = False\n\ntry:\n    import pyperclip\n    PYPERCLIP_AVAILABLE = True\nexcept ImportError:\n    print(\"\u26a0\ufe0f pyperclip \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d - \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u0431\u0443\u0444\u0435\u0440 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\")\n    PYPERCLIP_AVAILABLE = False\n\ntry:\n    from bs4 import BeautifulSoup\n    BEAUTIFULSOUP_AVAILABLE = True\nexcept ImportError:\n    print(\"\u26a0\ufe0f BeautifulSoup \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d - \u043f\u0430\u0440\u0441\u0438\u043d\u0433 HTML \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\")\n    BEAUTIFULSOUP_AVAILABLE = False\n\ntry:\n    from rapidfuzz import process, fuzz\n    RAPIDFUZZ_AVAILABLE = True\nexcept ImportError:\n    print(\"\u26a0\ufe0f rapidfuzz \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d - \u043d\u0435\u0447\u0435\u0442\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d\")\n    RAPIDFUZZ_AVAILABLE = False\n\n# \ud83c\udd95 \u0418\u041d\u0422\u0415\u0413\u0420\u0410\u0426\u0418\u042f \u0420\u0410\u0421\u0428\u0418\u0420\u0415\u041d\u041d\u041e\u0419 \u0421\u0418\u0421\u0422\u0415\u041c\u042b \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u041e\u0412\nprint(\"\ud83d\udd27 \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432...\")\n\n# Fallback \u0438\u043c\u043f\u043e\u0440\u0442\u044b \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439\ntry:\n    from translation_dict import FULL_TRANSLATION_DICT, TRANSLATION_DICT, ELECTRONICS_DICT, COMPUTER_DICT\nexcept ImportError:\n    # Fallback \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0435\u0441\u043b\u0438 \u0444\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\n    FULL_TRANSLATION_DICT = {\n        \"Camera\": \"\u041a\u0430\u043c\u0435\u0440\u0430\",\n        \"Lens\": \"\u041e\u0431\u044a\u0435\u043a\u0442\u0438\u0432\",\n        \"Video\": \"\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\", \n        \"Audio\": \"\u0410\u0443\u0434\u0438\u043e\",\n        \"Network\": \"\u0421\u0435\u0442\u044c\",\n        \"Power\": \"\u041f\u0438\u0442\u0430\u043d\u0438\u0435\",\n        \"Megapixel\": \"\u041c\u043f\",\n        \"Auto\": \"\u0410\u0432\u0442\u043e\",\n        \"Manual\": \"\u0420\u0443\u0447\u043d\u0430\u044f\",\n        \"Supported\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\"\n    }\n\n# \u0418\u043c\u043f\u043e\u0440\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0441 fallback\ntry:\n    from translation_integration import (\n        IntegratedTranslationSystem,\n        IntegratedTranslationDialog,\n        TRANSLATION_AVAILABLE as ADVANCED_TRANSLATION_AVAILABLE,\n        is_advanced_system_available\n    )\n    \n    print(\"\u2705 \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430\")\n    TRANSLATION_INTEGRATION_SUCCESS = True\n    \n    # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b\n    TranslationSystem = IntegratedTranslationSystem\n    TranslationManagementDialog = IntegratedTranslationDialog\n    TRANSLATION_AVAILABLE = ADVANCED_TRANSLATION_AVAILABLE\n    \n    if is_advanced_system_available():\n        print(\"\ud83c\udf1f \u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0430\")\n        TRANSLATION_SYSTEM_TYPE = \"advanced\"\n    else:\n        print(\"\u2699\ufe0f \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u0430\")\n        TRANSLATION_SYSTEM_TYPE = \"basic_integrated\"\n        \nexcept ImportError as e:\n    print(f\"\u26a0\ufe0f \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430: {e}\")\n    print(\"\ud83d\udd04 \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0430 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443...\")\n    TRANSLATION_INTEGRATION_SUCCESS = False\n    TRANSLATION_SYSTEM_TYPE = \"builtin\"\n\n    # \ud83c\udf10 \u0412\u0421\u0422\u0420\u041e\u0415\u041d\u041d\u0410\u042f \u0411\u0410\u0417\u041e\u0412\u0410\u042f \u0421\u0418\u0421\u0422\u0415\u041c\u0410 \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u041e\u0412 (FALLBACK)\n    class BuiltinTranslationSystem:\n        \"\"\"\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0441 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c\u0438 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043c\u0438 \u0438\u0437 \u0432\u0430\u0448\u0438\u0445 \u0442\u0430\u0431\u043b\u0438\u0446\"\"\"\n        def __init__(self):\n            self.auto_translate = True\n            self.fuzzy_match_threshold = 80\n            self.translate_values = True\n            self.translate_parameters = True\n            self.custom_translations = {}\n            \n            # \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0432\u0430\u0448\u0438\u0445 \u0442\u0430\u0431\u043b\u0438\u0446 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u043a\u0430\u043c\u0435\u0440\u044b\n            self.translation_dict = {\n                # \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044b\n                \"Camera\": \"\u041a\u0430\u043c\u0435\u0440\u0430\",\n                \"Lens\": \"\u041e\u0431\u044a\u0435\u043a\u0442\u0438\u0432\", \n                \"Video\": \"\u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\",\n                \"Audio\": \"\u0410\u0443\u0434\u0438\u043e\",\n                \"Network\": \"\u0421\u0435\u0442\u044c\",\n                \"Alarm\": \"\u0422\u0440\u0435\u0432\u043e\u0433\u0430\",\n                \"Interfaces\": \"\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b\",\n                \"Environmental\": \"\u0423\u0441\u043b\u043e\u0432\u0438\u044f \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438\",\n                \"Electrical\": \"\u041f\u0438\u0442\u0430\u043d\u0438\u0435\",\n                \"Mechanical\": \"\u0413\u0430\u0431\u0430\u0440\u0438\u0442\u044b\",\n                \"Infrared\": \"\u0418\u041a \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0430\",\n                \n                # \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043a\u0430\u043c\u0435\u0440\u044b\n                \"Image Sensor\": \"\u041c\u0430\u0442\u0440\u0438\u0446\u0430\",\n                \"Pixels\": \"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439\",\n                \"Min. Illumination\": \"\u0427\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\",\n                \"Shutter Speed\": \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u0437\u0430\u0442\u0432\u043e\u0440\u0430\",\n                \"Day / Night\": \"\u0420\u0435\u0436\u0438\u043c \u00ab\u0414\u0435\u043d\u044c / \u041d\u043e\u0447\u044c\u00bb\",\n                \"Wide Dynamic Range\": \"\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\",\n                \"S/N Ratio\": \"\u0421\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u0441\u0438\u0433\u043d\u0430\u043b / \u0448\u0443\u043c\",\n                \"White Balance\": \"\u0420\u0435\u0433\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0430 \u0431\u0430\u043b\u0430\u043d\u0441 \u0431\u0435\u043b\u043e\u0433\u043e\",\n                \"Gain Control\": \"\u0420\u0435\u0433\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0430 \u0443\u0441\u0438\u043b\u0435\u043d\u0438\u044f\",\n                \"Backlight Compensation\": \"\u0423\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\",\n                \n                # \u041e\u0431\u044a\u0435\u043a\u0442\u0438\u0432\n                \"Mount Type\": \"\u041a\u0440\u0435\u043f\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0438\u0432\u0430\",\n                \"Auto Iris\": \"\u0414\u0438\u0430\u0444\u0440\u0430\u0433\u043c\u0430\",\n                \"Focal Length\": \"\u0424\u043e\u043a\u0443\u0441\u043d\u043e\u0435 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\",\n                \"Focus Control\": \"\u0424\u043e\u043a\u0443\u0441\u0438\u0440\u043e\u0432\u043a\u0430\",\n                \"Angle of View\": \"\u0423\u0433\u043e\u043b \u043e\u0431\u0437\u043e\u0440\u0430\",\n                \"Adjustment Range\": \"\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430\",\n                \n                # \u0418\u041a \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0430\n                \"IR Distance\": \"\u0414\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0418\u041a \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0438\",\n                \"IR Intensity\": \"\u0422\u0438\u043f \u0418\u041a \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0438\",\n                \n                # \u0412\u0438\u0434\u0435\u043e\n                \"Compression\": \"\u0424\u043e\u0440\u043c\u0430\u0442 \u0441\u0436\u0430\u0442\u0438\u044f\",\n                \"Image Resolution\": \"\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\",\n                \"Frame Rate\": \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432\",\n                \"Bitrate\": \"\u0411\u0438\u0442\u0440\u0435\u0439\u0442\",\n                \"Bitrate Control\": \"\u0422\u0438\u043f \u0431\u0438\u0442\u0440\u0435\u0439\u0442\u0430\",\n                \"Multi-stream\": \"\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0442\u043e\u043a\",\n                \"Caption\": \"\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438\",\n                \"Privacy Masking\": \"\u041c\u0430\u0441\u043a\u0430 \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e\u0441\u0442\u0438\",\n                \"Motion Detection\": \"\u0414\u0435\u0442\u0435\u043a\u0442\u043e\u0440 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f\",\n                \"Video Freeze\": \"Video Freeze\",\n                \"ROI\": \"ROI\",\n                \"Digital Noise Reduction\": \"\u0426\u0438\u0444\u0440\u043e\u0432\u043e\u0435 \u0448\u0443\u043c\u043e\u043f\u043e\u0434\u0430\u0432\u043b\u0435\u043d\u0438\u0435\",\n                \"Digital Defog\": \"\u0410\u043d\u0442\u0438\u0442\u0443\u043c\u0430\u043d\",\n                \"Image Flip\": \"\u0420\u0435\u0436\u0438\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\",\n                \n                # \u0410\u0443\u0434\u0438\u043e\n                \"Audio Function\": \"\u0424\u0443\u043d\u043a\u0446\u0438\u0438 \u0430\u0443\u0434\u0438\u043e\",\n                \n                # \u0421\u0435\u0442\u044c\n                \"Network Protocols\": \"\u0421\u0435\u0442\u0435\u0432\u044b\u0435 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u044b\",\n                \"User Interface\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043e\u0432\",\n                \"Max. User Access\": \"\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439\",\n                \"Application Programming\": \"\u0421\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c\",\n                \"Automatic Network Replenishment (ANR)\": \"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0434\u0435\u0442\u0435\u043a\u0446\u0438\u044f \u0441\u0435\u0442\u0435\u0432\u043e\u0433\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430 (ANR)\",\n                \n                # \u0422\u0440\u0435\u0432\u043e\u0433\u0430\n                \"Alarm Triggers\": \"\u0421\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u043e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u044f\",\n                \"Intelligent\": \"\u0418\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\",\n                \"Alarm Events\": \"\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u0438 \u0442\u0440\u0435\u0432\u043e\u0433\u0435\",\n                \n                # \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044b\n                \"Ethernet\": \"\u0421\u0435\u0442\u0435\u0432\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\",\n                \"Audio In / Out\": \"\u0410\u0443\u0434\u0438\u043e \u0432\u0445\u043e\u0434 / \u0432\u044b\u0445\u043e\u0434\",\n                \"Control\": \"\u041f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442. \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\",\n                \"Alarm In / Out\": \"\u0422\u0440\u0435\u0432\u043e\u0436\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 / \u0432\u044b\u0445\u043e\u0434\",\n                \"Video Out\": \"\u0412\u0438\u0434\u0435\u043e \u0432\u044b\u0445\u043e\u0434\",\n                \"Memory Slot\": \"\u0421\u043b\u043e\u0442 \u0434\u043b\u044f \u043a\u0430\u0440\u0442\u044b \u043f\u0430\u043c\u044f\u0442\u0438\",\n                \n                # \u0423\u0441\u043b\u043e\u0432\u0438\u044f \u044d\u043a\u0441\u043f\u043b\u0443\u0430\u0442\u0430\u0446\u0438\u0438\n                \"Operating Temperature\": \"\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\",\n                \"Operating Humidity\": \"\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438\",\n                \"Protection Level\": \"\u0423\u0440\u043e\u0432\u0435\u043d\u044c \u0437\u0430\u0449\u0438\u0442\u044b\",\n                \n                # \u041f\u0438\u0442\u0430\u043d\u0438\u0435\n                \"Power\": \"\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u0438\u0442\u0430\u043d\u0438\u044f\",\n                \"Power Consumption\": \"\u041f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c\",\n                \n                # \u0413\u0430\u0431\u0430\u0440\u0438\u0442\u044b\n                \"Weight\": \"\u0412\u0435\u0441\",\n                \"Dimensions\": \"\u0420\u0430\u0437\u043c\u0435\u0440\u044b\",\n                \n                # \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n                \"Auto\": \"\u0410\u0432\u0442\u043e\",\n                \"Manual\": \"\u0420\u0443\u0447\u043d\u0430\u044f\",\n                \"Auto / Manual\": \"\u0410\u0432\u0442\u043e / \u0420\u0443\u0447\u043d\u0430\u044f\",\n                \"Supported\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\",\n                \"Megapixel\": \"\u041c\u043f\",\n                \"4.0 Megapixel\": \"4 \u041c\u043f\",\n                \"Digital WDR\": \"\u0426\u0438\u0444\u0440\u043e\u0432\u043e\u0439 WDR\",\n                \"Smart IR\": \"\u0421\u043c\u0430\u0440\u0442 \u0418\u041a\",\n                \"VBR / CBR\": \"VBR / CBR\",\n                \"3D noise reduction\": \"3D DNR\",\n                \"Bi-directional audio\": \"\u0414\u0432\u0443\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u044f\u044f \u0441\u0432\u044f\u0437\u044c\",\n                \"10/100M\": \"10M/100M\",\n                \"DC12V\": \"12\u0412 DC\",\n                \"PoE\": \"PoE\",\n                \"Max.\": \"\u0414\u043e\",\n                \"Up to\": \"\u0414\u043e\",\n                \"fps\": \"\u043a/\u0441\",\n                \"kbps\": \"\u043a\u0431\u0438\u0442/\u0441\", \n                \"Mbps\": \"\u041c\u0431\u0438\u0442/\u0441\",\n                \"mm\": \"\u043c\u043c\",\n                \"kg\": \"\u043a\u0433\",\n                \"GB\": \"\u0413\u0431\"\n            }\n        \n        def translate_text(self, text, is_parameter=True):\n            \"\"\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0442\u0435\u043a\u0441\u0442\u0430 \u0441 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u0433\u043e \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439\"\"\"\n            if not text or not isinstance(text, str):\n                return text\n            \n            # \u041f\u0440\u044f\u043c\u043e\u0439 \u043f\u043e\u0438\u0441\u043a\n            if text in self.translation_dict:\n                return self.translation_dict[text]\n            \n            # \u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0447\u0430\u0441\u0442\u044f\u043c (\u0434\u043b\u044f \u0441\u043e\u0441\u0442\u0430\u0432\u043d\u044b\u0445 \u0442\u0435\u0440\u043c\u0438\u043d\u043e\u0432)\n            words = text.split()\n            if len(words) > 1:\n                translated_words = []\n                for word in words:\n                    # \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0437\u043d\u0430\u043a\u0438 \u043f\u0440\u0435\u043f\u0438\u043d\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430\n                    clean_word = word.strip('.,;:()[]{}/')\n                    if clean_word in self.translation_dict:\n                        # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0437\u043d\u0430\u043a\u0438 \u043f\u0440\u0435\u043f\u0438\u043d\u0430\u043d\u0438\u044f\n                        punctuation = word[len(clean_word):]\n                        translated_words.append(self.translation_dict[clean_word] + punctuation)\n                    else:\n                        translated_words.append(word)\n                \n                result = \" \".join(translated_words)\n                if result != text:\n                    return result\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041f\u043e\u0438\u0441\u043a \u0426\u0415\u041b\u042b\u0425 \u0421\u041b\u041e\u0412 (\u043d\u0435 \u043f\u043e\u0434\u0441\u0442\u0440\u043e\u043a) \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n            import re\n            original_text = text\n            for original, translation in self.translation_dict.items():\n                # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0446\u0435\u043b\u043e\u0433\u043e \u0441\u043b\u043e\u0432\u0430\n                pattern = r'\\b' + re.escape(original) + r'\\b'\n                if re.search(pattern, text, re.IGNORECASE):\n                    old_text = text\n                    text = re.sub(pattern, translation, text, flags=re.IGNORECASE)\n                    if old_text != text:\n                        print(f\"\ud83d\udd04 \u0421\u043b\u043e\u0432\u0430\u0440\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 (\u0446\u0435\u043b\u043e\u0435 \u0441\u043b\u043e\u0432\u043e): '{original}' \u2192 '{translation}' \u0432 '{old_text}' \u2192 '{text}'\")\n            \n            return text\n        \n        def save_custom_translations(self):\n            \"\"\"\u0417\u0430\u0433\u043b\u0443\u0448\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438\"\"\"\n            pass\n        \n        def get_translation_count(self):\n            \"\"\"\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n            return len(self.translation_dict)\n\n    class BuiltinTranslationDialog:\n        \"\"\"\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435\"\"\"\n        def __init__(self, parent, translation_system):\n            system_info = {\n                'advanced': \"\ud83c\udf1f \u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\",\n                'basic_integrated': \"\u2699\ufe0f \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", \n                'builtin': \"\ud83d\udccb \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\n            }\n            \n            count = getattr(translation_system, 'get_translation_count', lambda: len(getattr(translation_system, 'translation_dict', {})))()\n            \n            info_text = f\"\"\"{system_info.get(TRANSLATION_SYSTEM_TYPE, '\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432')}\n\n\ud83d\udcda \u0414\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {count}\n\ud83c\udfaf \u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434: {'\u0412\u043a\u043b\u044e\u0447\u0435\u043d' if getattr(translation_system, 'auto_translate', True) else '\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d'}\n\ud83d\udd0d \u041d\u0435\u0447\u0435\u0442\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a: {getattr(translation_system, 'fuzzy_match_threshold', 80)}%\n\n\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b:\n\u2022 Camera \u2192 \u041a\u0430\u043c\u0435\u0440\u0430\n\u2022 Image Sensor \u2192 \u041c\u0430\u0442\u0440\u0438\u0446\u0430  \n\u2022 Motion Detection \u2192 \u0414\u0435\u0442\u0435\u043a\u0442\u043e\u0440 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f\n\u2022 Power Consumption \u2192 \u041f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0435\u043c\u0430\u044f \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u044c\n\u2022 4.0 Megapixel \u2192 4 \u041c\u043f\n\u2022 Supported \u2192 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\n\n{\n'\u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 GUI \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c, \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u043c\u0438, \u0438\u043c\u043f\u043e\u0440\u0442\u043e\u043c/\u044d\u043a\u0441\u043f\u043e\u0440\u0442\u043e\u043c \u0438 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u043e\u0439.' \nif TRANSLATION_SYSTEM_TYPE == 'advanced' else\n'\u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0441 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u043c fallback. \u0414\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432.'\nif TRANSLATION_SYSTEM_TYPE == 'basic_integrated' else\n'\u0410\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0431\u0430\u0437\u043e\u0432\u044b\u043c \u043d\u0430\u0431\u043e\u0440\u043e\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432. \u0414\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u043f\u043e\u043b\u043d\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432.'\n}\"\"\"\n            \n            messagebox.showinfo(\"\ud83c\udf10 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", info_text)\n\n    # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c fallback \u043a\u043b\u0430\u0441\u0441\u044b\n    TranslationSystem = BuiltinTranslationSystem\n    TranslationManagementDialog = BuiltinTranslationDialog\n    TRANSLATION_AVAILABLE = True\n\n# \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\nsystem_info = {\n    'advanced': \"\ud83c\udf1f \u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0433\u043e\u0442\u043e\u0432\u0430 \u043a \u0440\u0430\u0431\u043e\u0442\u0435\",\n    'basic_integrated': \"\u2699\ufe0f \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0433\u043e\u0442\u043e\u0432\u0430 \u043a \u0440\u0430\u0431\u043e\u0442\u0435\", \n    'builtin': \"\ud83d\udccb \u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0433\u043e\u0442\u043e\u0432\u0430 \u043a \u0440\u0430\u0431\u043e\u0442\u0435\"\n}\nprint(system_info.get(TRANSLATION_SYSTEM_TYPE, \"\u2705 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0433\u043e\u0442\u043e\u0432\u0430 \u043a \u0440\u0430\u0431\u043e\u0442\u0435\"))\n\nfrom difflib import SequenceMatcher\nclass ParameterMergeDialog:\n    \"\"\"\u0414\u0438\u0430\u043b\u043e\u0433 \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043f\u0440\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438\"\"\"\n    def __init__(self, parent, parameters):\n        self.result = None\n        \n        self.dialog = Toplevel(parent)\n        self.dialog.title(\"\u0412\u044b\u0431\u043e\u0440 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\")\n        self.dialog.geometry(\"400x300\")\n        self.dialog.transient(parent)\n        self.dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        self.dialog.geometry(\"+%d+%d\" % (parent.winfo_rootx() + 50, parent.winfo_rooty() + 50))\n        \n        Label(self.dialog, text=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f:\", font=(\"Arial\", 10, \"bold\")).pack(pady=10)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        self.listbox = Listbox(self.dialog, height=8)\n        self.listbox.pack(fill=BOTH, expand=True, padx=20, pady=10)\n        \n        for param in parameters:\n            self.listbox.insert(END, param)\n        \n        # \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e\n        if parameters:\n            self.listbox.selection_set(0)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        button_frame = Frame(self.dialog)\n        button_frame.pack(pady=10)\n        \n        Button(button_frame, text=\"OK\", command=self.ok_clicked, width=10).pack(side=LEFT, padx=5)\n        Button(button_frame, text=\"\u041e\u0442\u043c\u0435\u043d\u0430\", command=self.cancel_clicked, width=10).pack(side=LEFT, padx=5)\n        \n        # \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 Enter \u0438 Escape\n        self.dialog.bind('<Return>', lambda e: self.ok_clicked())\n        self.dialog.bind('<Escape>', lambda e: self.cancel_clicked())\n        \n        # \u0416\u0434\u0435\u043c \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0434\u0438\u0430\u043b\u043e\u0433\u0430\n        self.dialog.wait_window()\n    \n    def ok_clicked(self):\n        selection = self.listbox.curselection()\n        if selection:\n            self.result = self.listbox.get(selection[0])\n        self.dialog.destroy()\n    \n    def cancel_clicked(self):\n        self.result = None\n        self.dialog.destroy()\n\n\nclass ValueSynonymsManager:\n    \"\"\"\ud83c\udd95 \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u042b\u0419 \u041c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 callbacks v4.9\"\"\"\n    def __init__(self):\n        self.synonyms = {}  # {synonym: preferred_value}\n        self.callbacks = []  # \ud83c\udd95 \u0421\u043f\u0438\u0441\u043e\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0434\u043b\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445\n        self.load_synonyms()\n    \n    def add_callback(self, callback_func):\n        \"\"\"\ud83c\udd95 \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430 \u0434\u043b\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445\"\"\"\n        if callback_func not in self.callbacks:\n            self.callbacks.append(callback_func)\n            print(f\"\ud83d\udcde \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d callback \u0434\u043b\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {callback_func.__name__}\")\n    \n    def remove_callback(self, callback_func):\n        \"\"\"\ud83c\udd95 \u0423\u0434\u0430\u043b\u044f\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430\"\"\"\n        if callback_func in self.callbacks:\n            self.callbacks.remove(callback_func)\n            print(f\"\ud83d\udcde \u0423\u0434\u0430\u043b\u0435\u043d callback \u0434\u043b\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {callback_func.__name__}\")\n    \n    def notify_callbacks(self):\n        \"\"\"\ud83c\udd95 \u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u0442 \u0432\u0441\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445\"\"\"\n        print(f\"\ud83d\udcde \u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c {len(self.callbacks)} callbacks \u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\")\n        for callback in self.callbacks:\n            try:\n                callback()\n                print(f\"\u2705 Callback \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d: {callback.__name__}\")\n            except Exception as e:\n                print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u0432 callback {callback.__name__}: {e}\")\n\n    def load_synonyms(self):\n        \"\"\"\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0438\u0437 \u0444\u0430\u0439\u043b\u0430\"\"\"\n        try:\n            if os.path.exists(\"value_synonyms.json\"):\n                with open(\"value_synonyms.json\", \"r\", encoding=\"utf-8\") as f:\n                    self.synonyms = json.load(f)\n            else:\n                # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e\n                self.synonyms = {\n                    \"Support\": \"Supported\",\n                    \"Yes\": \"Supported\", \n                    \"Enable\": \"Supported\",\n                    \"True\": \"Supported\",\n                    \"No\": \"Not Supported\",\n                    \"False\": \"Not Supported\",\n                    \"Disable\": \"Not Supported\"\n                }\n                self.save_synonyms()\n        except Exception as e:\n            print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {e}\")\n            self.synonyms = {}\n    \n    def save_synonyms(self):\n        \"\"\"\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0432 \u0444\u0430\u0439\u043b\"\"\"\n        try:\n            with open(\"value_synonyms.json\", \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.synonyms, f, ensure_ascii=False, indent=2)\n        except Exception as e:\n            print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {e}\")\n    \n    def add_synonym(self, synonym, preferred):\n        \"\"\"\ud83c\udd95 \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0438\u043d\u043e\u043d\u0438\u043c \u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432\"\"\"\n        self.synonyms[synonym.strip()] = preferred.strip()\n        self.save_synonyms()\n        self.notify_callbacks()  # \ud83c\udd95 \u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438\n    \n    def remove_synonym(self, synonym):\n        \"\"\"\ud83c\udd95 \u0423\u0434\u0430\u043b\u044f\u0435\u0442 \u0441\u0438\u043d\u043e\u043d\u0438\u043c \u0438 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u0442 \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u043e\u0432\"\"\"\n        if synonym in self.synonyms:\n            del self.synonyms[synonym]\n            self.save_synonyms()\n            self.notify_callbacks()  # \ud83c\udd95 \u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438\n    \n    def get_preferred_value(self, value):\n        \"\"\"\ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\"\"\"\n        if not value:\n            return value\n        \n        value = value.strip()\n        \n        # \u041f\u0440\u044f\u043c\u043e\u0439 \u043f\u043e\u0438\u0441\u043a\n        if value in self.synonyms:\n            result = self.synonyms[value]\n            print(f\"\ud83d\udd04 \u0421\u0438\u043d\u043e\u043d\u0438\u043c \u043d\u0430\u0439\u0434\u0435\u043d: '{value}' \u2192 '{result}'\")\n            return result\n        \n        # \u041f\u043e\u0438\u0441\u043a \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n        value_lower = value.lower()\n        for synonym, preferred in self.synonyms.items():\n            if synonym.lower() == value_lower:\n                print(f\"\ud83d\udd04 \u0421\u0438\u043d\u043e\u043d\u0438\u043c \u043d\u0430\u0439\u0434\u0435\u043d (\u0440\u0435\u0433\u0438\u0441\u0442\u0440): '{value}' \u2192 '{preferred}'\")\n                return preferred\n        \n        return value\n    \n    def merge_synonyms_in_list(self, values):\n        \"\"\"\ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        if not values:\n            return []\n        \n        print(f\"\ud83d\udd04 \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430: {values}\")\n        \n        # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0437\u0430\u043c\u0435\u043d\u044b \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n        processed_values = []\n        seen_lower = set()\n        \n        for value in values:\n            # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n            cleaned_value = clean_value_enhanced(value)\n            \n            # \u041f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n            if not is_valid_value_enhanced(cleaned_value):\n                print(f\"\u26a0\ufe0f \u041f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043e \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: '{value}'\")\n                continue\n            \n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n            preferred = self.get_preferred_value(cleaned_value)\n            preferred_cleaned = clean_value_enhanced(preferred)\n            \n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n            preferred_lower = preferred_cleaned.lower()\n            if preferred_lower not in seen_lower:\n                seen_lower.add(preferred_lower)\n                processed_values.append(preferred_cleaned)\n                print(f\"\u2705 \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: '{preferred_cleaned}'\")\n            else:\n                print(f\"\ud83d\udd04 \u0414\u0443\u0431\u043b\u0438\u043a\u0430\u0442 \u043f\u0440\u043e\u043f\u0443\u0449\u0435\u043d: '{preferred_cleaned}'\")\n        \n        print(f\"\u2705 \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {processed_values}\")\n        return processed_values\n\n\nclass ValueSynonymsDialog:\n    \"\"\"\u0414\u0438\u0430\u043b\u043e\u0433 \u0434\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n    def __init__(self, parent, synonyms_manager):\n        self.synonyms_manager = synonyms_manager\n        self.result = False\n        \n        self.dialog = Toplevel(parent)\n        self.dialog.title(\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\")\n        self.dialog.geometry(\"600x400\")\n        self.dialog.transient(parent)\n        self.dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        self.dialog.geometry(\"+%d+%d\" % (parent.winfo_rootx() + 50, parent.winfo_rooty() + 50))\n        \n        self.create_widgets()\n        self.refresh_list()\n        \n        # \u0416\u0434\u0435\u043c \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0434\u0438\u0430\u043b\u043e\u0433\u0430\n        self.dialog.wait_window()\n    \n    def create_widgets(self):\n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\n        Label(self.dialog, text=\"\ud83c\udd95 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 (\u0441 \u0430\u0432\u0442\u043e\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u043c)\", font=(\"Arial\", 12, \"bold\")).pack(pady=10)\n        Label(self.dialog, text=\"\u0421\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043d\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e!\", font=(\"Arial\", 9), fg=\"blue\").pack(pady=5)\n        \n        # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0440\u0435\u0439\u043c\n        main_frame = Frame(self.dialog)\n        main_frame.pack(fill=BOTH, expand=True, padx=20, pady=10)\n        \n        # \u041b\u0435\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c - \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n        left_frame = Frame(main_frame)\n        left_frame.pack(side=LEFT, fill=BOTH, expand=True, padx=(0, 10))\n        \n        Label(left_frame, text=\"\u0422\u0435\u043a\u0443\u0449\u0438\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u044b:\", font=(\"Arial\", 10, \"bold\")).pack(anchor=W)\n        \n        # \u0421\u043f\u0438\u0441\u043e\u043a \u0441 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u043e\u0439\n        list_frame = Frame(left_frame)\n        list_frame.pack(fill=BOTH, expand=True, pady=5)\n        \n        scrollbar = Scrollbar(list_frame)\n        scrollbar.pack(side=RIGHT, fill=Y)\n        \n        self.synonyms_listbox = Listbox(list_frame, yscrollcommand=scrollbar.set)\n        self.synonyms_listbox.pack(side=LEFT, fill=BOTH, expand=True)\n        scrollbar.config(command=self.synonyms_listbox.yview)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\n        Button(left_frame, text=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439\", command=self.remove_synonym).pack(pady=5)\n        \n        # \u041f\u0440\u0430\u0432\u0430\u044f \u0447\u0430\u0441\u0442\u044c - \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n        right_frame = Frame(main_frame)\n        right_frame.pack(side=RIGHT, fill=Y, padx=(10, 0))\n        \n        Label(right_frame, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0438\u043d\u043e\u043d\u0438\u043c:\", font=(\"Arial\", 10, \"bold\")).pack(anchor=W)\n        \n        Label(right_frame, text=\"\u0421\u0438\u043d\u043e\u043d\u0438\u043c (\u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0435\u043d\u043e):\").pack(anchor=W, pady=(10, 0))\n        self.synonym_entry = Entry(right_frame, width=25)\n        self.synonym_entry.pack(fill=X, pady=2)\n        \n        Label(right_frame, text=\"\u041f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435:\").pack(anchor=W, pady=(10, 0))\n        self.preferred_entry = Entry(right_frame, width=25)\n        self.preferred_entry.pack(fill=X, pady=2)\n        \n        Button(right_frame, text=\"\u2795 \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\", command=self.add_synonym).pack(pady=10)\n        \n        # \u041f\u0440\u0438\u043c\u0435\u0440\u044b\n        examples_frame = LabelFrame(right_frame, text=\"\u041f\u0440\u0438\u043c\u0435\u0440\u044b\", padx=5, pady=5)\n        examples_frame.pack(fill=X, pady=10)\n        \n        Label(examples_frame, text=\"Support \u2192 Supported\", font=(\"Arial\", 8), fg=\"gray\").pack(anchor=W)\n        Label(examples_frame, text=\"Yes \u2192 Supported\", font=(\"Arial\", 8), fg=\"gray\").pack(anchor=W)\n        Label(examples_frame, text=\"Enable \u2192 Supported\", font=(\"Arial\", 8), fg=\"gray\").pack(anchor=W)\n        \n        # \ud83c\udd95 \u0418\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439\n        status_frame = LabelFrame(right_frame, text=\"\ud83d\udd04 \u0421\u0442\u0430\u0442\u0443\u0441\", padx=5, pady=5)\n        status_frame.pack(fill=X, pady=5)\n        \n        Label(status_frame, text=\"\u2705 \u0410\u0432\u0442\u043e\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\", font=(\"Arial\", 8), fg=\"green\").pack(anchor=W)\n        Label(status_frame, text=\"\u0418\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0441\u0440\u0430\u0437\u0443\", font=(\"Arial\", 8), fg=\"gray\").pack(anchor=W)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        button_frame = Frame(self.dialog)\n        button_frame.pack(pady=10)\n        \n        Button(button_frame, text=\"\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u044c\", command=self.ok_clicked, width=15).pack(side=LEFT, padx=5)\n        Button(button_frame, text=\"\u274c \u041e\u0442\u043c\u0435\u043d\u0430\", command=self.cancel_clicked, width=15).pack(side=LEFT, padx=5)\n    \n    def refresh_list(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\"\"\"\n        self.synonyms_listbox.delete(0, END)\n        for synonym, preferred in sorted(self.synonyms_manager.synonyms.items()):\n            self.synonyms_listbox.insert(END, f\"{synonym} \u2192 {preferred}\")\n    \n    def add_synonym(self):\n        \"\"\"\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u044b\u0439 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\"\"\"\n        synonym = self.synonym_entry.get().strip()\n        preferred = self.preferred_entry.get().strip()\n        \n        if not synonym or not preferred:\n            messagebox.showwarning(\"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435\", \"\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043e\u0431\u0430 \u043f\u043e\u043b\u044f\")\n            return\n        \n        if synonym == preferred:\n            messagebox.showwarning(\"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435\", \"\u0421\u0438\u043d\u043e\u043d\u0438\u043c \u0438 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c\u0438\")\n            return\n        \n        # \ud83c\udd95 \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0438\u043d\u043e\u043d\u0438\u043c (\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u0438\u0442 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435)\n        self.synonyms_manager.add_synonym(synonym, preferred)\n        self.refresh_list()\n        \n        self.synonym_entry.delete(0, END)\n        self.preferred_entry.delete(0, END)\n        \n        messagebox.showinfo(\"\u2705 \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e\", f\"\u0421\u0438\u043d\u043e\u043d\u0438\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d: {synonym} \u2192 {preferred}\\n\\n\ud83d\udd04 \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438!\")\n    \n    def remove_synonym(self):\n        \"\"\"\u0423\u0434\u0430\u043b\u044f\u0435\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\"\"\"\n        selection = self.synonyms_listbox.curselection()\n        if not selection:\n            messagebox.showwarning(\"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\")\n            return\n        \n        item_text = self.synonyms_listbox.get(selection[0])\n        synonym = item_text.split(\" \u2192 \")[0]\n        \n        # \ud83c\udd95 \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0438\u043d\u043e\u043d\u0438\u043c (\u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u0438\u0442 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435)\n        self.synonyms_manager.remove_synonym(synonym)\n        self.refresh_list()\n        \n        messagebox.showinfo(\"\u2705 \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e\", f\"\u0421\u0438\u043d\u043e\u043d\u0438\u043c \u0443\u0434\u0430\u043b\u0435\u043d: {synonym}\\n\\n\ud83d\udd04 \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438!\")\n    \n    def ok_clicked(self):\n        self.result = True\n        self.dialog.destroy()\n    \n    def cancel_clicked(self):\n        self.result = False\n        self.dialog.destroy()\n\n\ndef normalize_variant(val):\n    val = re.sub(r'\\s*\\(\\s*', ' (', val)\n    val = re.sub(r'\\s*\\)\\s*', ') ', val)\n    val = re.sub(r'\\s+', ' ', val).strip()\n    return val\n\n\ndef normalize_value_format(val):\n    \"\"\"\n    \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439:\n    - \u0423\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u043e\u0431\u0435\u043b\u044b \u0432\u043e\u043a\u0440\u0443\u0433 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 (~, \u00d7, >, <, \u0438 \u0434\u0440.)\n    - \u041f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u044b \u043a \u0432\u0435\u0440\u0445\u043d\u0435\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0443\n    - \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u0431\u0435\u043b\u044b \u043c\u0435\u0436\u0434\u0443 \u0447\u0438\u0441\u043b\u0430\u043c\u0438 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n    \"\"\"\n    if not val or not isinstance(val, str):\n        return val\n    \n    # \u0421\u043f\u0438\u0441\u043e\u043a \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0439 \u0434\u043b\u044f IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f)\n    units = [\n        # \u0411\u0430\u0437\u043e\u0432\u044b\u0435 \u0444\u0438\u0437\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0432\u0435\u043b\u0438\u0447\u0438\u043d\u044b\n        'mm', 'cm', 'm', 'km', 'in', 'ft', 'yard', '\u03bcm', 'nm', 'pm',  # \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\n        'mg', 'g', 'kg', 'lb', 'oz',  # \u0432\u0435\u0441\n        'ml', 'l', 'gal', 'pt', 'qt',  # \u043e\u0431\u044a\u0435\u043c\n        \n        # \u0412\u0440\u0435\u043c\u044f \u0438 \u0447\u0430\u0441\u0442\u043e\u0442\u0430\n        's', 'ms', '\u03bcs', 'ns', 'min', 'h', 'day',  # \u0432\u0440\u0435\u043c\u044f\n        'hz', 'khz', 'mhz', 'ghz',  # \u0447\u0430\u0441\u0442\u043e\u0442\u0430\n        'fps',  # \u0447\u0430\u0441\u0442\u043e\u0442\u0430 \u043a\u0430\u0434\u0440\u043e\u0432\n        \n        # \u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n        'v', 'mv', 'kv', 'a', 'ma', '\u03bca', 'w', 'mw', 'kw', 'wh', 'kwh',  # \u044d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\n        'ohm', '\u03c9', 'kohm', 'mohm',  # \u0441\u043e\u043f\u0440\u043e\u0442\u0438\u0432\u043b\u0435\u043d\u0438\u0435\n        'f', 'mf', '\u03bcf', 'nf', 'pf',  # \u0435\u043c\u043a\u043e\u0441\u0442\u044c\n        \n        # \u0414\u0430\u043d\u043d\u044b\u0435 \u0438 \u0441\u0435\u0442\u0438\n        'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb',  # \u0440\u0430\u0437\u043c\u0435\u0440 \u0434\u0430\u043d\u043d\u044b\u0445\n        'bps', 'kbps', 'mbps', 'gbps', 'tbps',  # \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438\n        'bit', 'byte',  # \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0434\u0430\u043d\u043d\u044b\u0445\n        \n        # \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0438 \u0432\u0438\u0434\u0435\u043e\n        'mp', 'px', 'dpi', 'ppi', 'ppc',  # \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\n        'lpi', 'lpmm',  # \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\n        \n        # \u0417\u0432\u0443\u043a \u0438 \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435\n        'db', 'dba', 'dbc', 'dbi', 'dbm', 'dbw', 'dbfs',  # \u0434\u0435\u0446\u0438\u0431\u0435\u043b\u044b\n        'lux', 'lm', 'cd', 'cd/m\u00b2', 'nit', 'fL',  # \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\n        \n        # \u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0442\u043e\u043b\u044c\u043a\u043e \u0426\u0435\u043b\u044c\u0441\u0438\u0439!)\n        '\u00b0', '\u00b0C', 'k',  # \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430 (\u0443\u0431\u0440\u0430\u043b\u0438 \u00b0f \u0438 \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043b\u0438 \u00b0c \u043d\u0430 \u00b0C)\n        \n        # \u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c\n        'pa', 'kpa', 'mpa', 'gpa', 'bar', 'mbar', 'psi', 'atm', 'torr', 'mmhg',  # \u0434\u0430\u0432\u043b\u0435\u043d\u0438\u0435\n        'rh', '%rh',  # \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c\n        \n        # IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u0435\n        'fov', 'aov', 'hfov', 'vfov',  # \u043f\u043e\u043b\u0435 \u0437\u0440\u0435\u043d\u0438\u044f\n        'ir', 'uv',  # \u0441\u043f\u0435\u043a\u0442\u0440\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u044b\n        'poe', 'poe+', 'poe++',  # \u043f\u0438\u0442\u0430\u043d\u0438\u0435 \u043f\u043e Ethernet\n        \n        # \u041f\u0440\u043e\u0447\u0438\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b\n        '%', 'ppm', 'ppb', 'ppt',  # \u0434\u043e\u043b\u0438\n        'rpm', 'rps',  # \u043e\u0431\u043e\u0440\u043e\u0442\u044b\n        'g-force', 'g'  # \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435\n    ]\n    \n    # \u0421\u043f\u0438\u0441\u043e\u043a \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0445 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\n    abbreviations = [\n        'onvif', 'poe', 'ip', 'tcp', 'udp', 'http', 'https', 'ftp', 'smtp',\n        'dns', 'dhcp', 'wifi', 'lan', 'wan', 'usb', 'hdmi', 'vga', 'dvi',\n        'led', 'lcd', 'oled', 'ccd', 'cmos', 'ir', 'uv', 'rgb', 'yuv',\n        'pal', 'ntsc', 'mpeg', 'h264', 'h265', 'mjpeg', 'jpeg', 'png',\n        'pdf', 'xml', 'json', 'api', 'sdk', 'gui', 'cli', 'os', 'ios',\n        'ptz', 'dvr', 'nvr', 'hdd', 'ssd', 'ram', 'cpu', 'gpu',\n        'ac', 'dc', 'rms', 'snr', 'thd', 'agc', 'awb', 'aec', 'roi',\n        'rtsp', 'rtmp', 'sip', 'p2p', 'ddns', 'upnp', 'ntp', 'snmp'\n    ]\n    \n    result = val\n    \n    # 1. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432 \u0432\u043e\u043a\u0440\u0443\u0433 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432\n    symbols = ['~', '\u00d7', '>', '<', '\u2265', '\u2264', '\u00b1', '\u00f7', '\u2212', '-', '+', '=']\n    for symbol in symbols:\n        # \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u043f\u0440\u043e\u0431\u0435\u043b\u044b \u0432\u043e\u043a\u0440\u0443\u0433 \u0441\u0438\u043c\u0432\u043e\u043b\u0430, \u0437\u0430\u0442\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435\n        pattern = rf'\\s*{re.escape(symbol)}\\s*'\n        result = re.sub(pattern, f' {symbol} ', result)\n    \n    # 2. \u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u043b\u044f \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u043e\u0432 (\u0447\u0438\u0441\u0435\u043b \u0441 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438)\n    # \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \"808nm ~ 940nm\" -> \"808 nm ~ 940 nm\"\n    for unit in units:\n        # \u041f\u0430\u0442\u0442\u0435\u0440\u043d \u0434\u043b\u044f \u0447\u0438\u0441\u043b\u0430 \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u0435\u043b\u0430 \u043f\u0435\u0440\u0435\u0434 \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439\n        pattern = rf'(\\d+(?:\\.\\d+)?)\\s*{re.escape(unit)}(?!\\w)'\n        replacement = rf'\\1 {unit}'\n        result = re.sub(pattern, replacement, result, flags=re.IGNORECASE)\n    \n    # 3. \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u043e\u0432 \u0442\u0438\u043f\u0430 \"1/30,000s\" -> \"1/30,000 s\"\n    # \u041f\u0430\u0442\u0442\u0435\u0440\u043d \u0434\u043b\u044f \u0434\u0440\u043e\u0431\u0435\u0439 \u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b \u0441 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438\n    pattern = r'(\\d+(?:[,./]\\d+)*)\\s*([a-zA-Z]+)(?!\\w)'\n    def replace_number_unit(match):\n        number = match.group(1)\n        unit_candidate = match.group(2).lower()\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u044d\u0442\u043e \u0435\u0434\u0438\u043d\u0438\u0446\u0435\u0439 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n        if unit_candidate in [u.lower() for u in units]:\n            return f'{number} {unit_candidate}'\n        else:\n            return match.group(0)  # \u041e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u0430\u043a \u0435\u0441\u0442\u044c\n    \n    result = re.sub(pattern, replace_number_unit, result)\n    \n    # 4. \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440 \u043a \u0432\u0435\u0440\u0445\u043d\u0435\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0443\n    for abbr in abbreviations:\n        # \u0418\u0449\u0435\u043c \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u0443 \u043a\u0430\u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0441\u043b\u043e\u0432\u043e (\u0441 \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u043c\u0438 \u0441\u043b\u043e\u0432)\n        pattern = rf'\\b{re.escape(abbr)}\\b'\n        result = re.sub(pattern, abbr.upper(), result, flags=re.IGNORECASE)\n    \n    # 5. \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0441\u0438\u043c\u0432\u043e\u043b\u0430 \u0433\u0440\u0430\u0434\u0443\u0441\u0430 \u0441 \u0447\u0438\u0441\u043b\u0430\u043c\u0438\n    # \"102,5\u00b0\" -> \"102,5 \u00b0\", \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u0431\u0443\u043a\u0432\u044b \u043f\u043e\u0441\u043b\u0435 (\u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0442\u0440\u043e\u0433\u0430\u0442\u044c \u00b0C)\n    result = re.sub(r'(\\d+(?:[,.]\\d+)?)\\s*\u00b0(?![a-zA-Z])', r'\\1 \u00b0', result)\n    \n    # 6. \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u043b\u0438\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u0431\u0435\u043b\u044b\n    result = re.sub(r'\\s+', ' ', result).strip()\n    \n    return result\n\n\ndef clean_value_enhanced(value):\n    \"\"\"\ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0423\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u0442 \u043c\u0443\u0441\u043e\u0440\u0430\"\"\"\n    if not value:\n        return \"\"\n    \n    value = str(value).strip()\n    \n    # \u0423\u0434\u0430\u043b\u044f\u0435\u043c HTML \u0442\u0435\u0433\u0438\n    value = re.sub(r'<[^>]+>', '', value)\n    \n    # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u043b\u0438\u0448\u043d\u0438\u0435 \u043f\u0440\u043e\u0431\u0435\u043b\u044b\n    value = re.sub(r'\\s+', ' ', value).strip()\n    \n    # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n    if value in ['/', ';', '/;', '/', '&nbsp;', '-', '\u2013', '\u2014', '...', '.', ',', ':-', ':']:\n        return \"\"\n    \n    # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u0449\u0438\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432-\u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439\n    if re.match(r'^[/;,\\s\\-\u2013\u2014\\.\\:]+$', value):\n        return \"\"\n    \n    return value\n\n\ndef is_valid_value_enhanced(value):\n    \"\"\"\ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c\"\"\"\n    if not value:\n        return False\n    \n    value = clean_value_enhanced(value)\n    if not value:\n        return False\n    \n    # \u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0434\u043b\u0438\u043d\u0430 \u0434\u043b\u044f \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n    if len(value.strip()) < 1:\n        return False\n    \n    return True\n\n\nclass ColumnSelectorDialog:\n    def __init__(self, parent, columns, selected_columns=None):\n        self.parent = parent\n        self.columns = columns\n        self.selected_columns = selected_columns or []\n        self.result = None\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e\n        self.dialog = Toplevel(parent)\n        self.dialog.title(\"\u0412\u044b\u0431\u043e\u0440 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\")\n        self.dialog.geometry(\"500x600\")\n        self.dialog.grab_set()\n        self.dialog.transient(parent)\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043e\u043a\u043d\u043e\n        self.dialog.geometry(\"+%d+%d\" % (parent.winfo_rootx() + 50, parent.winfo_rooty() + 50))\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\n        self.setup_ui()\n        \n    def setup_ui(self):\n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\n        title_frame = Frame(self.dialog)\n        title_frame.pack(fill=X, padx=10, pady=10)\n        \n        Label(title_frame, text=\"\u0412\u044b\u0431\u043e\u0440 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\", font=(\"Arial\", 12, \"bold\")).pack()\n        Label(title_frame, text=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u0438\u0437 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430 \u0434\u043b\u044f \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\", \n              font=(\"Arial\", 9)).pack(pady=(5, 0))\n        \n        # \u0418\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\n        self.status_label = Label(title_frame, text=\"\", font=(\"Arial\", 9), fg=\"blue\")\n        self.status_label.pack(pady=(5, 0))\n        \n        # \u0424\u0440\u0435\u0439\u043c \u0441 \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u0430\u043c\u0438 \u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0431\u0430\u0440\u043e\u043c\n        frame = Frame(self.dialog)\n        frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        canvas = Canvas(frame)\n        scrollbar = Scrollbar(frame, orient=\"vertical\", command=canvas.yview)\n        scrollable_frame = Frame(canvas)\n        \n        scrollable_frame.bind(\n            \"<Configure>\",\n            lambda e: canvas.configure(scrollregion=canvas.bbox(\"all\"))\n        )\n        \n        canvas.create_window((0, 0), window=scrollable_frame, anchor=\"nw\")\n        canvas.configure(yscrollcommand=scrollbar.set)\n        \n        # \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0447\u0435\u043a\u0431\u043e\u0441\u043e\u0432\n        self.check_vars = {}\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u044b \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u0430\n        for i, col in enumerate(self.columns, 1):\n            var = BooleanVar()\n            # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u0430 \u0435\u0441\u043b\u0438 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u0440\u0430\u043d\u0435\u0435\n            if col in self.selected_columns:\n                var.set(True)\n            \n            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c callback \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0443\u0441\u0430\n            var.trace('w', self.update_status)\n            \n            self.check_vars[col] = var\n            cb = Checkbutton(scrollable_frame, text=f\"{i}. {col}\", variable=var, anchor=W, justify=LEFT)\n            cb.pack(fill=X, padx=5, pady=1)\n        \n        canvas.pack(side=\"left\", fill=\"both\", expand=True)\n        scrollbar.pack(side=\"right\", fill=\"y\")\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        btn_frame = Frame(self.dialog)\n        btn_frame.pack(fill=X, padx=10, pady=5)\n        \n        Button(btn_frame, text=\"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435\", command=self.select_all).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u0421\u043d\u044f\u0442\u044c \u0432\u0441\u0435\", command=self.deselect_all).pack(side=LEFT, padx=5)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 OK/\u041e\u0442\u043c\u0435\u043d\u0430\n        btn_frame2 = Frame(self.dialog)\n        btn_frame2.pack(fill=X, padx=10, pady=10)\n        \n        Button(btn_frame2, text=\"\u041e\u041a\", command=self.ok_clicked, width=10).pack(side=RIGHT, padx=5)\n        Button(btn_frame2, text=\"\u041e\u0442\u043c\u0435\u043d\u0430\", command=self.cancel_clicked, width=10).pack(side=RIGHT)\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435\n        self.update_status()\n        \n        # \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043b\u0430\u0432\u0438\u0448\n        self.dialog.bind('<Return>', lambda e: self.ok_clicked())\n        self.dialog.bind('<Escape>', lambda e: self.cancel_clicked())\n        \n    def update_status(self, *args):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\"\"\"\n        selected_count = sum(1 for var in self.check_vars.values() if var.get())\n        total_count = len(self.check_vars)\n        self.status_label.config(text=f\"\u0412\u044b\u0431\u0440\u0430\u043d\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432: {selected_count} \u0438\u0437 {total_count}\")\n        \n    def select_all(self):\n        \"\"\"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b\"\"\"\n        for var in self.check_vars.values():\n            var.set(True)\n            \n    def deselect_all(self):\n        \"\"\"\u0421\u043d\u044f\u0442\u044c \u0432\u044b\u0431\u043e\u0440 \u0441\u043e \u0432\u0441\u0435\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\"\"\"\n        for var in self.check_vars.values():\n            var.set(False)\n        \n    def ok_clicked(self):\n        self.selected_columns = [col for col, var in self.check_vars.items() if var.get()]\n        self.result = self.selected_columns\n        self.dialog.destroy()\n        \n    def cancel_clicked(self):\n        self.result = None\n        self.dialog.destroy()\n\n\nclass ToolTip:\n    def __init__(self, widget, text):\n        self.widget = widget\n        self.text = text\n        self.tipwindow = None\n        self.id = None\n        self.x = self.y = 0\n\n        self.widget.bind('<Enter>', self.enter)\n        self.widget.bind('<Leave>', self.leave)\n\n    def enter(self, event=None):\n        if self.tipwindow:\n            return\n        x, y, cx, cy = self.widget.bbox(\"insert\")\n        x = x + self.widget.winfo_rootx() + 25\n        y = y + cy + self.widget.winfo_rooty() + 25\n        self.tipwindow = tw = Toplevel(self.widget)\n        tw.wm_overrideredirect(1)\n        tw.wm_geometry(\"+%d+%d\" % (x, y))\n        label = Label(tw, text=self.text, justify=LEFT,\n                      background=\"#ffffe0\", relief=SOLID, borderwidth=1,\n                      font=(\"tahoma\", \"8\", \"normal\"))\n        label.pack(ipadx=1)\n\n    def leave(self, event=None):\n        tw = self.tipwindow\n        self.tipwindow = None\n        if tw:\n            tw.destroy()\n\n\nclass CaseNormalizationDialog:\n    def __init__(self, parent, case_normalization, save_callback):\n        self.parent = parent\n        self.case_normalization = case_normalization.copy()\n        self.save_callback = save_callback\n        \n        self.top = Toplevel(parent)\n        self.top.title(\"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\")\n        self.top.geometry(\"800x600\")\n        self.top.grab_set()\n        \n        # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0440\u0435\u0439\u043c\n        main_frame = Frame(self.top)\n        main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)\n        \n        # \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\n        info_text = \"\"\"\u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440 \u0434\u043b\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439.\n\u0412\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u0432 \u0440\u0430\u0437\u043d\u043e\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \"Support\", \"SUPPORT\", \"support\") \u0431\u0443\u0434\u0443\u0442 \u0441\u0447\u0438\u0442\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c\u0438.\n\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u0442\u043e\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0438\u0442\u043e\u0433\u043e\u0432\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435.\"\"\"\n        \n        info_label = Label(main_frame, text=info_text, justify=LEFT, wraplength=750)\n        info_label.pack(pady=(0, 10))\n        \n        # \u0424\u0440\u0435\u0439\u043c \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        add_frame = LabelFrame(main_frame, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\")\n        add_frame.pack(fill=X, pady=(0, 10))\n        \n        Label(add_frame, text=\"\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435:\").grid(row=0, column=0, sticky=W, padx=5, pady=5)\n        self.new_value_entry = Entry(add_frame, width=40)\n        self.new_value_entry.grid(row=0, column=1, sticky=W+E, padx=5, pady=5)\n        \n        self.add_button = Button(add_frame, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\", command=self.add_value)\n        self.add_button.grid(row=0, column=2, padx=5, pady=5)\n        \n        add_frame.grid_columnconfigure(1, weight=1)\n        \n        # \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        table_frame = LabelFrame(main_frame, text=\"\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\")\n        table_frame.pack(fill=BOTH, expand=True)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c Treeview \u0441 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u043e\u0439\n        tree_frame = Frame(table_frame)\n        tree_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n        \n        self.tree = ttk.Treeview(tree_frame, columns=('value',), show='headings')\n        self.tree.heading('value', text='\u041f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440')\n        self.tree.column('value', width=300)\n        \n        vsb = Scrollbar(tree_frame, orient=\"vertical\", command=self.tree.yview)\n        self.tree.configure(yscrollcommand=vsb.set)\n        \n        self.tree.pack(side=LEFT, fill=BOTH, expand=True)\n        vsb.pack(side=RIGHT, fill=Y)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        self.refresh_tree()\n        \n        # \u041f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u044f\n        self.tree.bind('<Delete>', self.delete_selected)\n        self.tree.bind('<Double-1>', self.edit_selected)\n        self.new_value_entry.bind('<Return>', lambda e: self.add_value())\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        btn_frame = Frame(table_frame)\n        btn_frame.pack(fill=X, padx=5, pady=5)\n        \n        Button(btn_frame, text=\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", command=self.edit_selected).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=self.delete_selected).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435\", command=self.clear_all).pack(side=LEFT, padx=15)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0434\u0438\u0430\u043b\u043e\u0433\u0430\n        dialog_btn_frame = Frame(self.top)\n        dialog_btn_frame.pack(fill=X, pady=10)\n        \n        Button(dialog_btn_frame, text=\"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=self.save_and_close).pack(side=RIGHT, padx=5)\n        Button(dialog_btn_frame, text=\"\u041e\u0442\u043c\u0435\u043d\u0430\", command=self.top.destroy).pack(side=RIGHT)\n        \n    def refresh_tree(self):\n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e\n        for item in self.tree.get_children():\n            self.tree.delete(item)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u0438\u0437 \u0441\u043b\u043e\u0432\u0430\u0440\u044f\n        for value in sorted(self.case_normalization.keys()):\n            self.tree.insert('', END, values=(value,))\n    \n    def add_value(self):\n        value = self.new_value_entry.get().strip()\n        if not value:\n            return\n        \n        # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u0441\u043b\u043e\u0432\u0430\u0440\u044c (\u0441\u0430\u043c\u043e \u043d\u0430 \u0441\u0435\u0431\u044f)\n        self.case_normalization[value] = value\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        self.refresh_tree()\n        \n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043f\u043e\u043b\u0435 \u0432\u0432\u043e\u0434\u0430\n        self.new_value_entry.delete(0, END)\n    \n    def edit_selected(self, event=None):\n        selection = self.tree.selection()\n        if not selection:\n            return\n        \n        item = selection[0]\n        old_value = self.tree.item(item, 'values')[0]\n        \n        # \u0414\u0438\u0430\u043b\u043e\u0433 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n        new_value = simpledialog.askstring(\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\", \n                                          \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435:\", \n                                          initialvalue=old_value)\n        if new_value and new_value.strip():\n            new_value = new_value.strip()\n            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0440\u043e\u0435 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u043e\u0435\n            del self.case_normalization[old_value]\n            self.case_normalization[new_value] = new_value\n            self.refresh_tree()\n    \n    def delete_selected(self, event=None):\n        selection = self.tree.selection()\n        if not selection:\n            return\n        \n        for item in selection:\n            value = self.tree.item(item, 'values')[0]\n            if value in self.case_normalization:\n                del self.case_normalization[value]\n        \n        self.refresh_tree()\n    \n    def clear_all(self):\n        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f?\"):\n            self.case_normalization.clear()\n            self.refresh_tree()\n    \n    def save_and_close(self):\n        self.save_callback(self.case_normalization)\n        self.top.destroy()\n\n\nclass ValueTranslationRuleDialog:\n    \"\"\"\u0414\u0438\u0430\u043b\u043e\u0433 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f/\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n    def __init__(self, parent, rule=None):\n        self.parent = parent\n        self.result = None\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u043e\u0434\u0430\u043b\u044c\u043d\u043e\u0435 \u043e\u043a\u043d\u043e\n        self.dialog = Toplevel(parent)\n        self.dialog.title(\"\u041f\u0440\u0430\u0432\u0438\u043b\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\" if not rule else \"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\")\n        self.dialog.geometry(\"600x400\")\n        self.dialog.transient(parent)\n        self.dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        self.dialog.geometry(\"+%d+%d\" % (parent.winfo_rootx() + 100, parent.winfo_rooty() + 100))\n        \n        # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435\n        self.rule_data = rule.copy() if rule else {\n            \"active\": True,\n            \"original\": \"\",\n            \"translation\": \"\",\n            \"category\": \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435\",\n            \"description\": \"\",\n            \"examples\": \"\"\n        }\n        \n        self.create_widgets()\n        self.load_data()\n        \n        # \u0416\u0434\u0435\u043c \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u0434\u0438\u0430\u043b\u043e\u0433\u0430\n        self.dialog.wait_window()\n    \n    def create_widgets(self):\n        \"\"\"\u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u0432\u0438\u0434\u0436\u0435\u0442\u044b \u0434\u0438\u0430\u043b\u043e\u0433\u0430\"\"\"\n        main_frame = Frame(self.dialog)\n        main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)\n        \n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\n        header_frame = Frame(main_frame)\n        header_frame.pack(fill=X, pady=(0, 15))\n        \n        Label(header_frame, text=\"\ud83c\udf10 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\", \n              font=(\"Arial\", 12, \"bold\")).pack()\n        Label(header_frame, text=\"\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0441\u0442\u043e\u043b\u0431\u0446\u0435 '\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f'\",\n              font=(\"Arial\", 9), fg=\"gray\").pack()\n        \n        # \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u043e\u043b\u044f\n        fields_frame = LabelFrame(main_frame, text=\"\ud83d\udcdd \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\", font=(\"Arial\", 10, \"bold\"))\n        fields_frame.pack(fill=X, pady=(0, 10))\n        \n        # \u0410\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c\n        active_frame = Frame(fields_frame)\n        active_frame.pack(fill=X, padx=10, pady=5)\n        \n        self.active_var = BooleanVar(value=True)\n        Checkbutton(active_frame, text=\"\u2713 \u041f\u0440\u0430\u0432\u0438\u043b\u043e \u0430\u043a\u0442\u0438\u0432\u043d\u043e\", variable=self.active_var,\n                   font=(\"Arial\", 10)).pack(anchor=W)\n        \n        # \u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n        orig_frame = Frame(fields_frame)\n        orig_frame.pack(fill=X, padx=10, pady=5)\n        \n        Label(orig_frame, text=\"\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435:\", width=20, anchor=W).pack(side=LEFT)\n        self.original_entry = Entry(orig_frame, font=(\"Arial\", 10))\n        self.original_entry.pack(side=LEFT, fill=X, expand=True, padx=(5, 0))\n        \n        # \u041f\u0435\u0440\u0435\u0432\u043e\u0434\n        trans_frame = Frame(fields_frame)\n        trans_frame.pack(fill=X, padx=10, pady=5)\n        \n        Label(trans_frame, text=\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434:\", width=20, anchor=W).pack(side=LEFT)\n        self.translation_entry = Entry(trans_frame, font=(\"Arial\", 10))\n        self.translation_entry.pack(side=LEFT, fill=X, expand=True, padx=(5, 0))\n        \n        # \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\n        cat_frame = Frame(fields_frame)\n        cat_frame.pack(fill=X, padx=10, pady=5)\n        \n        Label(cat_frame, text=\"\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f:\", width=20, anchor=W).pack(side=LEFT)\n        self.category_var = StringVar()\n        self.category_combo = ttk.Combobox(cat_frame, textvariable=self.category_var, \n                                         values=[\"\u0414\u043b\u0438\u043d\u0430\", \"\u0412\u0435\u0441\", \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\", \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430\", \n                                               \"\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\", \"\u0422\u0435\u0440\u043c\u0438\u043d\u044b\", \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435\", \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435\"],\n                                         font=(\"Arial\", 10))\n        self.category_combo.pack(side=LEFT, fill=X, expand=True, padx=(5, 0))\n        \n        # \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u043e\u043b\u044f\n        details_frame = LabelFrame(main_frame, text=\"\ud83d\udccb \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", font=(\"Arial\", 10, \"bold\"))\n        details_frame.pack(fill=BOTH, expand=True, pady=(0, 10))\n        \n        # \u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\n        desc_frame = Frame(details_frame)\n        desc_frame.pack(fill=X, padx=10, pady=5)\n        \n        Label(desc_frame, text=\"\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435:\", anchor=W).pack(anchor=W)\n        self.description_entry = Entry(desc_frame, font=(\"Arial\", 10))\n        self.description_entry.pack(fill=X, pady=(2, 0))\n        \n        # \u041f\u0440\u0438\u043c\u0435\u0440\u044b\n        examples_frame = Frame(details_frame)\n        examples_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        Label(examples_frame, text=\"\u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f:\", anchor=W).pack(anchor=W)\n        \n        text_frame = Frame(examples_frame)\n        text_frame.pack(fill=BOTH, expand=True, pady=(2, 0))\n        \n        self.examples_text = Text(text_frame, height=4, font=(\"Arial\", 9))\n        examples_scroll = Scrollbar(text_frame, orient=VERTICAL, command=self.examples_text.yview)\n        self.examples_text.configure(yscrollcommand=examples_scroll.set)\n        \n        self.examples_text.pack(side=LEFT, fill=BOTH, expand=True)\n        examples_scroll.pack(side=RIGHT, fill=Y)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        buttons_frame = Frame(main_frame)\n        buttons_frame.pack(fill=X, pady=(10, 0))\n        \n        Button(buttons_frame, text=\"\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=self.save_rule,\n               bg=\"#E8F5E8\", font=(\"Arial\", 10, \"bold\")).pack(side=RIGHT, padx=(5, 0))\n        \n        Button(buttons_frame, text=\"\u274c \u041e\u0442\u043c\u0435\u043d\u0430\", command=self.cancel,\n               bg=\"#FFE6E6\", font=(\"Arial\", 10)).pack(side=RIGHT, padx=(5, 5))\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0430 \u043f\u043e\u043c\u043e\u0449\u0438\n        Button(buttons_frame, text=\"\u2753 \u041f\u043e\u043c\u043e\u0449\u044c\", command=self.show_help,\n               bg=\"#FFF4E6\", font=(\"Arial\", 10)).pack(side=LEFT)\n        \n        # \u041f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\n        self.original_entry.bind('<KeyRelease>', self.auto_generate_example)\n        self.translation_entry.bind('<KeyRelease>', self.auto_generate_example)\n    \n    def load_data(self):\n        \"\"\"\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u043e\u043b\u044f\"\"\"\n        self.active_var.set(self.rule_data.get(\"active\", True))\n        self.original_entry.insert(0, self.rule_data.get(\"original\", \"\"))\n        self.translation_entry.insert(0, self.rule_data.get(\"translation\", \"\"))\n        self.category_var.set(self.rule_data.get(\"category\", \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435\"))\n        self.description_entry.insert(0, self.rule_data.get(\"description\", \"\"))\n        self.examples_text.insert(\"1.0\", self.rule_data.get(\"examples\", \"\"))\n    \n    def auto_generate_example(self, event=None):\n        \"\"\"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u0438 \u0432\u0432\u043e\u0434\u0435\"\"\"\n        original = self.original_entry.get().strip()\n        translation = self.translation_entry.get().strip()\n        \n        if original and translation:\n            # \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\n            if re.match(r'^[a-zA-Z]+$', original):  # \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n                example = f\"100{original} \u2192 100 {translation}\"\n            else:\n                example = f\"{original} \u2192 {translation}\"\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u043f\u0443\u0441\u0442\u043e\u0435\n            current_examples = self.examples_text.get(\"1.0\", \"end-1c\").strip()\n            if not current_examples:\n                self.examples_text.delete(\"1.0\", END)\n                self.examples_text.insert(\"1.0\", example)\n    \n    def save_rule(self):\n        \"\"\"\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u043e\"\"\"\n        # \u0412\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f\n        original = self.original_entry.get().strip()\n        translation = self.translation_entry.get().strip()\n        \n        if not original:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\")\n            self.original_entry.focus()\n            return\n        \n        if not translation:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\")\n            self.translation_entry.focus()\n            return\n        \n        # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435\n        self.result = {\n            \"active\": self.active_var.get(),\n            \"original\": original,\n            \"translation\": translation,\n            \"category\": self.category_var.get() or \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435\",\n            \"description\": self.description_entry.get().strip() or f\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434 '{original}' \u2192 '{translation}'\",\n            \"examples\": self.examples_text.get(\"1.0\", \"end-1c\").strip() or f\"{original} \u2192 {translation}\"\n        }\n        \n        self.dialog.destroy()\n    \n    def cancel(self):\n        \"\"\"\u041e\u0442\u043c\u0435\u043d\u044f\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\"\"\"\n        self.result = None\n        self.dialog.destroy()\n    \n    def show_help(self):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u043f\u0440\u0430\u0432\u043a\u0443\"\"\"\n        help_text = \"\"\"\ud83c\udf10 \u0421\u043f\u0440\u0430\u0432\u043a\u0430 \u043f\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\n\ud83d\udccb \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u043e\u043b\u044f:\n\u2022 \u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 - \u0442\u0435\u043a\u0441\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442\u044c (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \"mm\", \"Support\")\n\u2022 \u041f\u0435\u0440\u0435\u0432\u043e\u0434 - \u043d\u0430 \u0447\u0442\u043e \u0437\u0430\u043c\u0435\u043d\u044f\u0442\u044c (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \"\u043c\u043c\", \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430\")  \n\u2022 \u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f - \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430\n\n\ud83d\udcdd \u041f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b:\n\u2022 \u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u0432 \u0441\u0442\u043e\u043b\u0431\u0446\u0435 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\n\u2022 \u041f\u043e\u0438\u0441\u043a \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u0447\u043d\u043e \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u0443 (\u0441 \u0443\u0447\u0435\u0442\u043e\u043c \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430)\n\u2022 \u041d\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u044e\u0442\u0441\u044f\n\n\ud83d\udca1 \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f:\n\u2022 \u0415\u0434\u0438\u043d\u0438\u0446\u044b: \"mm\" \u2192 \"\u043c\u043c\", \"kg\" \u2192 \"\u043a\u0433\"\n\u2022 \u0422\u0435\u0440\u043c\u0438\u043d\u044b: \"Auto\" \u2192 \"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\" \n\u2022 \u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435: \"Yes\" \u2192 \"\u0414\u0430\", \"No\" \u2192 \"\u041d\u0435\u0442\"\n\n\ud83d\udd0d \u0410\u0432\u0442\u043e\u043f\u043e\u0438\u0441\u043a \u0435\u0434\u0438\u043d\u0438\u0446:\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \"\u041d\u0430\u0439\u0442\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u044b\" \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u044f \n\u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432 \u0432\u0430\u0448\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.\"\"\"\n        \n        messagebox.showinfo(\"\u0421\u043f\u0440\u0430\u0432\u043a\u0430\", help_text)\n\n\nclass ReplacementRulesEditorDialog:\n    def __init__(self, parent, replacement_rules, save_callback):\n        self.parent = parent\n        self.replacement_rules = replacement_rules.copy()\n        self.save_callback = save_callback\n        \n        self.top = Toplevel(parent)\n        self.top.title(\"\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043f\u0440\u0430\u0432\u0438\u043b \u0437\u0430\u043c\u0435\u043d\u044b\")\n        self.top.geometry(\"900x700\")\n        self.top.grab_set()\n        \n        # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0440\u0435\u0439\u043c\n        main_frame = Frame(self.top)\n        main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)\n        \n        # \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\n        info_text = \"\"\"\u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439.\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 Python-\u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441 \u0434\u043b\u044f \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439.\n\u041f\u0440\u0438\u043c\u0435\u0440\u044b:\n- \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u043a\u043e\u0431\u043e\u043a: \u041f\u0430\u0442\u0442\u0435\u0440\u043d: \\\\([^\\\\)]*\\\\)  \u0417\u0430\u043c\u0435\u043d\u0430: (\u043f\u0443\u0441\u0442\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430)\n- \u0417\u0430\u043c\u0435\u043d\u0430 \"Yes\" \u043d\u0430 \"\u0414\u0430\": \u041f\u0430\u0442\u0442\u0435\u0440\u043d: \\\\bYes\\\\b  \u0417\u0430\u043c\u0435\u043d\u0430: \u0414\u0430\n- \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432: \u041f\u0430\u0442\u0442\u0435\u0440\u043d: \\\\s+  \u0417\u0430\u043c\u0435\u043d\u0430: (\u043e\u0434\u0438\u043d \u043f\u0440\u043e\u0431\u0435\u043b)\"\"\"\n        \n        info_label = Label(main_frame, text=info_text, justify=LEFT, wraplength=850)\n        info_label.pack(pady=(0, 10))\n        \n        # \u0424\u0440\u0435\u0439\u043c \u0434\u043b\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n        add_frame = LabelFrame(main_frame, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e\")\n        add_frame.pack(fill=X, pady=(0, 10))\n        \n        Label(add_frame, text=\"\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435:\").grid(row=0, column=0, sticky=W, padx=5, pady=2)\n        self.name_entry = Entry(add_frame, width=30)\n        self.name_entry.grid(row=0, column=1, sticky=W+E, padx=5, pady=2)\n        \n        Label(add_frame, text=\"\u0410\u043a\u0442\u0438\u0432\u043d\u043e:\").grid(row=0, column=2, sticky=W, padx=5, pady=2)\n        self.active_var = BooleanVar(value=True)\n        self.active_check = Checkbutton(add_frame, variable=self.active_var)\n        self.active_check.grid(row=0, column=3, sticky=W, padx=5, pady=2)\n        \n        Label(add_frame, text=\"\u041f\u0430\u0442\u0442\u0435\u0440\u043d (regex):\").grid(row=1, column=0, sticky=W, padx=5, pady=2)\n        self.pattern_entry = Entry(add_frame, width=60)\n        self.pattern_entry.grid(row=1, column=1, columnspan=3, sticky=W+E, padx=5, pady=2)\n        \n        Label(add_frame, text=\"\u0417\u0430\u043c\u0435\u043d\u0430:\").grid(row=2, column=0, sticky=W, padx=5, pady=2)\n        self.replacement_entry = Entry(add_frame, width=60)\n        self.replacement_entry.grid(row=2, column=1, columnspan=3, sticky=W+E, padx=5, pady=2)\n        \n        self.add_button = Button(add_frame, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u043e\", command=self.add_rule)\n        self.add_button.grid(row=3, column=0, columnspan=4, pady=10)\n        \n        add_frame.grid_columnconfigure(1, weight=1)\n        \n        # \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u0430\u0432\u0438\u043b\n        table_frame = LabelFrame(main_frame, text=\"\u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\")\n        table_frame.pack(fill=BOTH, expand=True)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c Treeview \u0441 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u043e\u0439\n        tree_frame = Frame(table_frame)\n        tree_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n        \n        columns = ('active', 'name', 'pattern', 'replacement')\n        self.tree = ttk.Treeview(tree_frame, columns=columns, show='headings')\n        self.tree.heading('active', text='\u0410\u043a\u0442\u0438\u0432\u043d\u043e')\n        self.tree.heading('name', text='\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435')\n        self.tree.heading('pattern', text='\u041f\u0430\u0442\u0442\u0435\u0440\u043d')\n        self.tree.heading('replacement', text='\u0417\u0430\u043c\u0435\u043d\u0430')\n        \n        self.tree.column('active', width=60, anchor=CENTER)\n        self.tree.column('name', width=150)\n        self.tree.column('pattern', width=300)\n        self.tree.column('replacement', width=200)\n        \n        vsb = Scrollbar(tree_frame, orient=\"vertical\", command=self.tree.yview)\n        self.tree.configure(yscrollcommand=vsb.set)\n        \n        self.tree.pack(side=LEFT, fill=BOTH, expand=True)\n        vsb.pack(side=RIGHT, fill=Y)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        self.refresh_tree()\n        \n        # \u041f\u0440\u0438\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u044f\n        self.tree.bind('<Delete>', self.delete_selected)\n        self.tree.bind('<Double-1>', self.edit_selected)\n        self.tree.bind('<Button-1>', self.on_click)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        btn_frame = Frame(table_frame)\n        btn_frame.pack(fill=X, padx=5, pady=5)\n        \n        Button(btn_frame, text=\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", command=self.edit_selected).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=self.delete_selected).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u0432\u0435\u0440\u0445\", command=self.move_up).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u041f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u0437\", command=self.move_down).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435\", command=self.clear_all).pack(side=LEFT, padx=15)\n        \n        # \ud83c\udd95 \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043d\u043e\u043f\u043a\u0438\n        btn_frame2 = Frame(table_frame)\n        btn_frame2.pack(fill=X, padx=5, pady=5)\n        \n        Button(btn_frame2, text=\"\ud83d\udce5 \u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\", command=self.import_rules).pack(side=LEFT, padx=5)\n        Button(btn_frame2, text=\"\ud83d\udce4 \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\", command=self.export_rules).pack(side=LEFT, padx=5)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0430 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n        test_frame = Frame(table_frame)\n        test_frame.pack(fill=X, padx=5, pady=5)\n        \n        Label(test_frame, text=\"\u0422\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430:\").pack(side=LEFT)\n        self.test_entry = Entry(test_frame, width=40)\n        self.test_entry.pack(side=LEFT, padx=5, fill=X, expand=True)\n        Button(test_frame, text=\"\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", command=self.test_rules).pack(side=LEFT, padx=5)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0434\u0438\u0430\u043b\u043e\u0433\u0430\n        dialog_btn_frame = Frame(self.top)\n        dialog_btn_frame.pack(fill=X, pady=10)\n        \n        Button(dialog_btn_frame, text=\"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=self.save_and_close).pack(side=RIGHT, padx=5)\n        Button(dialog_btn_frame, text=\"\u041e\u0442\u043c\u0435\u043d\u0430\", command=self.top.destroy).pack(side=RIGHT)\n        \n    def refresh_tree(self):\n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e\n        for item in self.tree.get_children():\n            self.tree.delete(item)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438\n        for rule in self.replacement_rules:\n            active_text = \"\u2713\" if rule.get('active', True) else \"\u2717\"\n            self.tree.insert('', END, values=(\n                active_text,\n                rule.get('name', ''),\n                rule.get('pattern', ''),\n                rule.get('replacement', '')\n            ))\n    \n    def on_click(self, event):\n        region = self.tree.identify(\"region\", event.x, event.y)\n        if region == \"cell\":\n            col = self.tree.identify_column(event.x)\n            row = self.tree.identify_row(event.y)\n            \n            if col == '#1':  # \u041a\u043e\u043b\u043e\u043d\u043a\u0430 \"\u0410\u043a\u0442\u0438\u0432\u043d\u043e\"\n                self.toggle_active(row)\n    \n    def toggle_active(self, row_id):\n        if not row_id:\n            return\n        \n        # \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u0438\u043d\u0434\u0435\u043a\u0441 \u0441\u0442\u0440\u043e\u043a\u0438\n        children = self.tree.get_children()\n        try:\n            index = children.index(row_id)\n            rule = self.replacement_rules[index]\n            rule['active'] = not rule.get('active', True)\n            self.refresh_tree()\n        except (ValueError, IndexError):\n            pass\n    \n    def add_rule(self):\n        name = self.name_entry.get().strip()\n        pattern = self.pattern_entry.get().strip()\n        replacement = self.replacement_entry.get()\n        \n        if not name or not pattern:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\")\n            return\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e\u0433\u043e \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\n        try:\n            re.compile(pattern)\n        except re.error as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435: {e}\")\n            return\n        \n        # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u043e\n        rule = {\n            'name': name,\n            'pattern': pattern,\n            'replacement': replacement,\n            'active': self.active_var.get()\n        }\n        self.replacement_rules.append(rule)\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        self.refresh_tree()\n        \n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043f\u043e\u043b\u044f \u0432\u0432\u043e\u0434\u0430\n        self.name_entry.delete(0, END)\n        self.pattern_entry.delete(0, END)\n        self.replacement_entry.delete(0, END)\n    \n    def edit_selected(self, event=None):\n        selection = self.tree.selection()\n        if not selection:\n            return\n        \n        # \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u0438\u043d\u0434\u0435\u043a\u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n        children = self.tree.get_children()\n        try:\n            index = children.index(selection[0])\n            rule = self.replacement_rules[index]\n        except (ValueError, IndexError):\n            return\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n        edit_dialog = Toplevel(self.top)\n        edit_dialog.title(\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\")\n        edit_dialog.geometry(\"600x200\")\n        edit_dialog.grab_set()\n        \n        Label(edit_dialog, text=\"\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435:\").grid(row=0, column=0, sticky=W, padx=5, pady=5)\n        name_var = StringVar(value=rule.get('name', ''))\n        name_entry = Entry(edit_dialog, textvariable=name_var, width=40)\n        name_entry.grid(row=0, column=1, sticky=W+E, padx=5, pady=5)\n        \n        Label(edit_dialog, text=\"\u041f\u0430\u0442\u0442\u0435\u0440\u043d:\").grid(row=1, column=0, sticky=W, padx=5, pady=5)\n        pattern_var = StringVar(value=rule.get('pattern', ''))\n        pattern_entry = Entry(edit_dialog, textvariable=pattern_var, width=40)\n        pattern_entry.grid(row=1, column=1, sticky=W+E, padx=5, pady=5)\n        \n        Label(edit_dialog, text=\"\u0417\u0430\u043c\u0435\u043d\u0430:\").grid(row=2, column=0, sticky=W, padx=5, pady=5)\n        replacement_var = StringVar(value=rule.get('replacement', ''))\n        replacement_entry = Entry(edit_dialog, textvariable=replacement_var, width=40)\n        replacement_entry.grid(row=2, column=1, sticky=W+E, padx=5, pady=5)\n        \n        active_var = BooleanVar(value=rule.get('active', True))\n        active_check = Checkbutton(edit_dialog, text=\"\u0410\u043a\u0442\u0438\u0432\u043d\u043e\", variable=active_var)\n        active_check.grid(row=3, column=1, sticky=W, padx=5, pady=5)\n        \n        def save_edit():\n            new_name = name_var.get().strip()\n            new_pattern = pattern_var.get().strip()\n            new_replacement = replacement_var.get()\n            \n            if not new_name or not new_pattern:\n                messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0417\u0430\u043f\u043e\u043b\u043d\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\")\n                return\n            \n            try:\n                re.compile(new_pattern)\n            except re.error as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435\u0432\u0435\u0440\u043d\u043e\u0435 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435: {e}\")\n                return\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u043e\n            rule['name'] = new_name\n            rule['pattern'] = new_pattern\n            rule['replacement'] = new_replacement\n            rule['active'] = active_var.get()\n            \n            self.refresh_tree()\n            edit_dialog.destroy()\n        \n        btn_frame = Frame(edit_dialog)\n        btn_frame.grid(row=4, column=0, columnspan=2, pady=10)\n        Button(btn_frame, text=\"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=save_edit).pack(side=RIGHT, padx=5)\n        Button(btn_frame, text=\"\u041e\u0442\u043c\u0435\u043d\u0430\", command=edit_dialog.destroy).pack(side=RIGHT)\n        \n        edit_dialog.grid_columnconfigure(1, weight=1)\n    \n    def delete_selected(self, event=None):\n        selection = self.tree.selection()\n        if not selection:\n            return\n        \n        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430?\"):\n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u044b \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432\n            children = self.tree.get_children()\n            indices = []\n            for item in selection:\n                try:\n                    indices.append(children.index(item))\n                except ValueError:\n                    pass\n            \n            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0432 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0441\u0431\u0438\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441\u044b\n            for index in sorted(indices, reverse=True):\n                del self.replacement_rules[index]\n            \n            self.refresh_tree()\n    \n    def move_up(self):\n        selection = self.tree.selection()\n        if not selection:\n            return\n        \n        children = self.tree.get_children()\n        try:\n            index = children.index(selection[0])\n            if index > 0:\n                # \u041c\u0435\u043d\u044f\u0435\u043c \u043c\u0435\u0441\u0442\u0430\u043c\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n                self.replacement_rules[index], self.replacement_rules[index-1] = \\\n                    self.replacement_rules[index-1], self.replacement_rules[index]\n                self.refresh_tree()\n                # \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\n                new_item = self.tree.get_children()[index-1]\n                self.tree.selection_set(new_item)\n        except (ValueError, IndexError):\n            pass\n    \n    def move_down(self):\n        selection = self.tree.selection()\n        if not selection:\n            return\n        \n        children = self.tree.get_children()\n        try:\n            index = children.index(selection[0])\n            if index < len(self.replacement_rules) - 1:\n                # \u041c\u0435\u043d\u044f\u0435\u043c \u043c\u0435\u0441\u0442\u0430\u043c\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n                self.replacement_rules[index], self.replacement_rules[index+1] = \\\n                    self.replacement_rules[index+1], self.replacement_rules[index]\n                self.refresh_tree()\n                # \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\n                new_item = self.tree.get_children()[index+1]\n                self.tree.selection_set(new_item)\n        except (ValueError, IndexError):\n            pass\n    \n    def clear_all(self):\n        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430?\"):\n            self.replacement_rules.clear()\n            self.refresh_tree()\n    \n    def test_rules(self):\n        test_string = self.test_entry.get()\n        if not test_string:\n            return\n        \n        result = test_string\n        applied_rules = []\n        \n        for rule in self.replacement_rules:\n            if not rule.get('active', True):\n                continue\n            \n            try:\n                old_result = result\n                result = re.sub(rule['pattern'], rule['replacement'], result)\n                if result != old_result:\n                    applied_rules.append(rule['name'])\n            except re.error:\n                continue\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\n        result_dialog = Toplevel(self.top)\n        result_dialog.title(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\")\n        result_dialog.geometry(\"500x300\")\n        \n        Label(result_dialog, text=\"\u0418\u0441\u0445\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430:\", font=(\"Arial\", 10, \"bold\")).pack(anchor=W, padx=10, pady=(10,0))\n        Label(result_dialog, text=test_string, wraplength=450, justify=LEFT).pack(anchor=W, padx=20)\n        \n        Label(result_dialog, text=\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:\", font=(\"Arial\", 10, \"bold\")).pack(anchor=W, padx=10, pady=(10,0))\n        Label(result_dialog, text=result, wraplength=450, justify=LEFT).pack(anchor=W, padx=20)\n        \n        if applied_rules:\n            Label(result_dialog, text=\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430:\", font=(\"Arial\", 10, \"bold\")).pack(anchor=W, padx=10, pady=(10,0))\n            for rule_name in applied_rules:\n                Label(result_dialog, text=f\"\u2022 {rule_name}\").pack(anchor=W, padx=20)\n        else:\n            Label(result_dialog, text=\"\u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u044b\", font=(\"Arial\", 10, \"bold\")).pack(anchor=W, padx=10, pady=(10,0))\n        \n        Button(result_dialog, text=\"\u0417\u0430\u043a\u0440\u044b\u0442\u044c\", command=result_dialog.destroy).pack(pady=10)\n    \n    def import_rules(self):\n        \"\"\"\u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u0438\u0437 JSON \u0444\u0430\u0439\u043b\u0430\"\"\"\n        try:\n            from tkinter import filedialog\n            \n            filename = filedialog.askopenfilename(\n                title=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0444\u0430\u0439\u043b \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438\",\n                filetypes=[(\"JSON \u0444\u0430\u0439\u043b\u044b\", \"*.json\"), (\"\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b\", \"*.*\")]\n            )\n            \n            if not filename:\n                return\n            \n            with open(filename, 'r', encoding='utf-8') as f:\n                imported_rules = json.load(f)\n            \n            if not isinstance(imported_rules, list):\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0444\u0430\u0439\u043b\u0430. \u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0430\u0432\u0438\u043b.\")\n                return\n            \n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u0430\u0432\u0438\u043b\n            valid_rules = []\n            for rule in imported_rules:\n                if isinstance(rule, dict) and 'name' in rule and 'pattern' in rule and 'replacement' in rule:\n                    # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c regex\n                    try:\n                        re.compile(rule['pattern'])\n                        valid_rules.append({\n                            'name': rule['name'],\n                            'pattern': rule['pattern'],\n                            'replacement': rule['replacement'],\n                            'active': rule.get('active', True)\n                        })\n                    except re.error:\n                        print(f\"\u041f\u0440\u043e\u043f\u0443\u0449\u0435\u043d\u043e \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e: {rule['name']}\")\n            \n            if not valid_rules:\n                messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412 \u0444\u0430\u0439\u043b\u0435 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b\")\n                return\n            \n            # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u0438\u043c\u043f\u043e\u0440\u0442\u0430\n            self.show_import_dialog(valid_rules)\n            \n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b: {e}\")\n    \n    def show_import_dialog(self, rules_to_import):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u0438\u043c\u043f\u043e\u0440\u0442\u0430\"\"\"\n        dialog = Toplevel(self.top)\n        dialog.title(\"\u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\")\n        dialog.geometry(\"700x500\")\n        dialog.transient(self.top)\n        dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        dialog.update_idletasks()\n        x = (dialog.winfo_screenwidth() // 2) - (dialog.winfo_width() // 2)\n        y = (dialog.winfo_screenheight() // 2) - (dialog.winfo_height() // 2)\n        dialog.geometry(f\"+{x}+{y}\")\n        \n        Label(dialog, text=f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e {len(rules_to_import)} \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u0438\u043c\u043f\u043e\u0440\u0442\u0430\", \n              font=(\"Arial\", 12, \"bold\")).pack(pady=10)\n        \n        # \u0424\u0440\u0435\u0439\u043c \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c\n        list_frame = Frame(dialog)\n        list_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        # \u0421\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0430\u0432\u0438\u043b \u0441 \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u0430\u043c\u0438\n        canvas = Canvas(list_frame)\n        scrollbar = Scrollbar(list_frame, orient=\"vertical\", command=canvas.yview)\n        scrollable_frame = Frame(canvas)\n        \n        scrollable_frame.bind(\n            \"<Configure>\",\n            lambda e: canvas.configure(scrollregion=canvas.bbox(\"all\"))\n        )\n        \n        canvas.create_window((0, 0), window=scrollable_frame, anchor=\"nw\")\n        canvas.configure(yscrollcommand=scrollbar.set)\n        \n        # \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u043e\u0432\n        check_vars = []\n        for i, rule in enumerate(rules_to_import):\n            var = BooleanVar(value=True)\n            check_vars.append(var)\n            \n            frame = Frame(scrollable_frame)\n            frame.pack(fill=X, pady=2)\n            \n            Checkbutton(frame, variable=var).pack(side=LEFT)\n            Label(frame, text=f\"{rule['name']}: {rule['pattern']} \u2192 {rule['replacement']}\", \n                  anchor=\"w\").pack(side=LEFT, fill=X, expand=True)\n        \n        canvas.pack(side=\"left\", fill=\"both\", expand=True)\n        scrollbar.pack(side=\"right\", fill=\"y\")\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        btn_frame = Frame(dialog)\n        btn_frame.pack(fill=X, padx=10, pady=10)\n        \n        def import_selected():\n            imported_count = 0\n            for i, var in enumerate(check_vars):\n                if var.get():\n                    rule = rules_to_import[i]\n                    # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 \u0443\u0436\u0435 \u0442\u0430\u043a\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e\n                    existing = any(r['name'] == rule['name'] for r in self.replacement_rules)\n                    if not existing:\n                        self.replacement_rules.append(rule)\n                        imported_count += 1\n            \n            if imported_count > 0:\n                self.refresh_tree()\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", f\"\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e {imported_count} \u043f\u0440\u0430\u0432\u0438\u043b\")\n            else:\n                messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041d\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\")\n            \n            dialog.destroy()\n        \n        Button(btn_frame, text=\"\u2713 \u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435\", \n               command=lambda: [var.set(True) for var in check_vars]).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u2717 \u0421\u043d\u044f\u0442\u044c \u0432\u0441\u0435\", \n               command=lambda: [var.set(False) for var in check_vars]).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\ud83d\udce5 \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435\", command=import_selected).pack(side=RIGHT, padx=5)\n        Button(btn_frame, text=\"\u274c \u041e\u0442\u043c\u0435\u043d\u0430\", command=dialog.destroy).pack(side=RIGHT, padx=5)\n    \n    def export_rules(self):\n        \"\"\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u0432 JSON \u0444\u0430\u0439\u043b\"\"\"\n        try:\n            from tkinter import filedialog\n            \n            if not self.replacement_rules:\n                messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u041d\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430\")\n                return\n            \n            filename = filedialog.asksaveasfilename(\n                title=\"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430\",\n                defaultextension=\".json\",\n                filetypes=[(\"JSON \u0444\u0430\u0439\u043b\u044b\", \"*.json\"), (\"\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b\", \"*.*\")]\n            )\n            \n            if not filename:\n                return\n            \n            with open(filename, 'w', encoding='utf-8') as f:\n                json.dump(self.replacement_rules, f, ensure_ascii=False, indent=2)\n            \n            messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", f\"\u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432 \u0444\u0430\u0439\u043b:\\\\n{filename}\")\n            \n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b: {e}\")\n    \n    def save_and_close(self):\n        self.save_callback(self.replacement_rules)\n        self.top.destroy()\n\n\nclass AttributeEditor:\n    MAPPINGS_FILE = 'mappings.json'\n    EXCLUSIONS_FILE = 'exclusions.json'\n    PROTECTED_VALUES_FILE = 'protected_values.json'  # \ud83c\udd95 \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n    SETTINGS_FILE = 'settings.json'\n    CASE_NORM_FILE = 'case_normalization.json'\n    REPLACEMENT_RULES_FILE = 'replacement_rules.json'\n    PARAM_MERGE_SETTINGS_FILE = 'param_merge_settings.json'\n    VALUE_MERGE_SETTINGS_FILE = 'value_merge_settings.json'\n\n    def __init__(self, root):\n        self.root = root\n        self.root.title(\"\ud83c\udd95 \u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 v4.9 - \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439\")\n        self.root.geometry(\"1200x700\")  # \u0423\u043c\u0435\u043d\u044c\u0448\u0438\u043b\u0438 \u0440\u0430\u0437\u043c\u0435\u0440 \u043e\u043a\u043d\u0430 \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0441\u0442\u0438\n        self.original_df = None\n        self.param_mapping = {}\n        self.original_values = {}\n        self.manual_split_values = {}\n        self.exclusions = []\n        self.mappings = {}\n        self.sort_directions = {}\n        self.edit_entry = None\n        self.edit_undo_stack = []\n        self.similar_groups = {}\n        self.SIMILARITY_THRESHOLD = 85\n        self.df_columns = []\n\n        self.mapping_sort_reverse = False\n        self._mapping_items_cache = []\n\n        self.settings = {\n            \"features_col\": \"\",\n            \"extra_cols\": [],\n            \"manual_split_values\": {},\n            \"source_file_path\": \"\",\n            \"output_folder_path\": \"\"\n        }\n\n        self.case_normalization = {}\n        self.manual_split_results = {}\n        self.replacement_rules = []\n        \n        # \ud83c\udd95 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0415 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u042f: \u043d\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0441\u0438\u043c\u0432\u043e\u043b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f\n        self.protected_values = []  # \u0421\u043f\u0438\u0441\u043e\u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0442\u0438\u043f\u0430 \"B/W\", \"I/O\", \"AC/DC\"\n\n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f - \u0430\u0432\u0442\u043e\u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435/\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430\n        self.param_merge_settings = {}\n        self.value_merge_settings = {}\n        \n        # \ud83c\udd95 \u041d\u041e\u0412\u042b\u0415 \u0412\u041e\u0417\u041c\u041e\u0416\u041d\u041e\u0421\u0422\u0418 v4.0\n        # \u041c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        self.synonyms_manager = ValueSynonymsManager()\n        \n        # \ud83c\udd95 \u0421\u0418\u0421\u0422\u0415\u041c\u0410 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u0418\u0425 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0419 v4.9\n        # \u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c callback \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n        self.synonyms_manager.add_callback(self.dynamic_update_main_table)\n        \n        # \ud83c\udd95 \u041d\u041e\u0412\u042b\u0415 \u0412\u041e\u0417\u041c\u041e\u0416\u041d\u041e\u0421\u0422\u0418 v4.7\n        # \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n        self.value_translation_rules = []\n        self.load_value_translation_rules()\n        \n        # \ud83c\udf10 \u0420\u0410\u0421\u0428\u0418\u0420\u0415\u041d\u041d\u0410\u042f \u0421\u0418\u0421\u0422\u0415\u041c\u0410 \u0415\u0414\u0418\u041d\u0418\u0426 \u0418\u0417\u041c\u0415\u0420\u0415\u041d\u0418\u0419 IP-\u0412\u0418\u0414\u0415\u041e\u041d\u0410\u0411\u041b\u042e\u0414\u0415\u041d\u0418\u042f\n        self.ip_surveillance_units = {}\n        self.load_ip_surveillance_units()\n        \n        # \ud83c\udf10 \u0418\u041d\u0418\u0426\u0418\u0410\u041b\u0418\u0417\u0410\u0426\u0418\u042f \u0421\u0418\u0421\u0422\u0415\u041c\u042b \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u041e\u0412\n        self.translation_available = TRANSLATION_AVAILABLE  # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043a\u0430\u043a \u0430\u0442\u0440\u0438\u0431\u0443\u0442 \u043a\u043b\u0430\u0441\u0441\u0430\n        # DEBUG: \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430\n        if self.translation_available:\n            self.translation_system = TranslationSystem()\n            print(\"\u2705 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u0430\")\n        else:\n            self.translation_system = None\n            print(\"\u26a0\ufe0f \u0420\u0430\u0431\u043e\u0442\u0430 \u0431\u0435\u0437 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\")\n        \n        # \u0426\u0432\u0435\u0442\u0430 \u0434\u043b\u044f \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        self.similar_param_colors = [\n            \"#FFE6E6\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u0440\u043e\u0437\u043e\u0432\u044b\u0439\n            \"#E6F3FF\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u0441\u0438\u043d\u0438\u0439  \n            \"#E6FFE6\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u0437\u0435\u043b\u0435\u043d\u044b\u0439\n            \"#FFF0E6\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u043e\u0440\u0430\u043d\u0436\u0435\u0432\u044b\u0439\n            \"#F0E6FF\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u0444\u0438\u043e\u043b\u0435\u0442\u043e\u0432\u044b\u0439\n            \"#FFFFE6\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u0436\u0435\u043b\u0442\u044b\u0439\n            \"#E6FFFF\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u0431\u0438\u0440\u044e\u0437\u043e\u0432\u044b\u0439\n            \"#FFE6F0\",  # \u0421\u0432\u0435\u0442\u043b\u043e-\u043c\u0430\u0433\u0435\u043d\u0442\u0430\n        ]\n        self.param_color_map = {}  # {param_name: color}\n        self.similar_param_groups = {}  # {representative: [similar_params]}\n\n        self.context_menu = Menu(self.root, tearoff=0)\n        self.context_menu.add_command(label=\"\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", command=self.copy_selected_value)\n        self.context_menu.add_separator()\n        self.context_menu.add_command(label=\"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\", command=self.merge_selected_similar_params)\n        self.context_menu.add_command(label=\"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", command=self.merge_selected_params_and_values)\n        \n        # \ud83c\udd95 \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u0434\u043b\u044f \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\n        self.params_context_menu = Menu(self.root, tearoff=0)\n        self.params_context_menu.add_command(label=\"\ud83d\udcdd \u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u0443\", command=self.edit_param_group)\n        self.params_context_menu.add_command(label=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b\", command=self.remove_from_param_group)\n        self.params_context_menu.add_separator()\n        self.params_context_menu.add_command(label=\"\u274c \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u044e \u0433\u0440\u0443\u043f\u043f\u0443\", command=self.delete_param_group)\n        \n        # \u0414\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0447\u0430\u0441\u0442\u043e\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        self.param_frequency = {}\n\n        self.load_settings()\n        self.load_mappings()\n        self.load_excl()\n        self.load_protected_values()  # \ud83c\udd95 \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        self.load_case_normalization()\n        self.load_replacement_rules()\n        self.load_merge_settings()\n        self.setup_ui()\n        \n        # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043a\u0435\u0448 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432\n        self.refresh_mapping_table()\n\n        self.root.protocol(\"WM_DELETE_WINDOW\", self.on_close)\n\n    def setup_ui(self):\n        \"\"\"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0433\u043e \u0438 \u044d\u0440\u0433\u043e\u043d\u043e\u043c\u0438\u0447\u043d\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\"\"\"\n        # DEBUG: \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\n        # \u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438\n        self.root.bind_all(\"<Control-c>\", self.global_ctrl_c)\n        self.root.bind_all(\"<Control-v>\", self.global_ctrl_v)\n        self.root.bind_all(\"<Control-z>\", self.global_ctrl_z)\n        self.root.bind_all(\"<Control-a>\", self.global_ctrl_a)\n        self.root.bind_all(\"<Delete>\", self.global_delete)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0435 \u043c\u0435\u043d\u044e\n        self.create_compact_menu()\n        \n        # \u0413\u043b\u0430\u0432\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\n        main_frame = Frame(self.root)\n        main_frame.pack(fill=BOTH, expand=True, padx=5, pady=2)\n\n        # \ud83d\udcc1 \u0413\u0420\u0423\u041f\u041f\u0410 1: \u0424\u0430\u0439\u043b\u044b \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 (\u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e \u0432 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435)\n        files_frame = LabelFrame(main_frame, text=\"\ud83d\udcc1 \u0424\u0430\u0439\u043b\u044b \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\", font=(\"Arial\", 9, \"bold\"))\n        files_frame.pack(fill=X, pady=(0, 2))\n        \n        # \u0421\u0442\u0440\u043e\u043a\u0430 1: \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0444\u0430\u0439\u043b\n        file_row1 = Frame(files_frame)\n        file_row1.pack(fill=X, padx=5, pady=2)\n        \n        Label(file_row1, text=\"\u0424\u0430\u0439\u043b:\", width=6).pack(side=LEFT)\n        self.source_file_var = StringVar(value=self.settings.get(\"source_file_path\", \"\"))\n        source_entry = Entry(file_row1, textvariable=self.source_file_var, width=35)\n        source_entry.pack(side=LEFT, fill=X, expand=True, padx=2)\n        source_entry.bind('<FocusOut>', lambda e: self.save_settings())\n        Button(file_row1, text=\"\ud83d\udcc2\", width=3, command=self.browse_source_file).pack(side=LEFT, padx=2)\n        ToolTip(source_entry, \"\u041f\u0443\u0442\u044c \u043a \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c\u0443 Excel \u0444\u0430\u0439\u043b\u0443\")\n        \n        # \u0412\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0441\u0442\u0440\u043e\u043a\u0438: \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n        Label(file_row1, text=\"\u0421\u0442\u043e\u043b\u0431\u0435\u0446:\", width=8).pack(side=LEFT, padx=(15, 2))\n        self.features_col = Entry(file_row1, width=4)\n        self.features_col.pack(side=LEFT)\n        self.features_col.insert(0, self.settings.get(\"features_col\", \"\"))\n        ToolTip(self.features_col, \"\u041d\u043e\u043c\u0435\u0440 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438\")\n        \n        Label(file_row1, text=\"\u0414\u043e\u043f.\u0441\u0442\u043e\u043b\u0431\u0446\u044b:\", width=10).pack(side=LEFT, padx=(15, 2))\n        self.extra_cols_label = Label(file_row1, text=\"\", width=12, anchor=W, relief=SUNKEN, bg=\"white\")\n        self.extra_cols_label.pack(side=LEFT, padx=2)\n        Button(file_row1, text=\"\u2699\ufe0f\", width=3, command=self.show_extra_cols_dialog).pack(side=LEFT, padx=2)\n        ToolTip(self.extra_cols_label, \"\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\")\n        \n        # \u0421\u0442\u0440\u043e\u043a\u0430 2: \u041f\u0430\u043f\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f (\u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e)\n        file_row2 = Frame(files_frame)\n        file_row2.pack(fill=X, padx=5, pady=(0, 2))\n        \n        Label(file_row2, text=\"\u041f\u0430\u043f\u043a\u0430:\", width=6).pack(side=LEFT)\n        self.output_folder_var = StringVar(value=self.settings.get(\"output_folder_path\", \"\"))\n        output_entry = Entry(file_row2, textvariable=self.output_folder_var, width=35)\n        output_entry.pack(side=LEFT, fill=X, expand=True, padx=2)\n        output_entry.bind('<FocusOut>', lambda e: self.save_settings())\n        Button(file_row2, text=\"\ud83d\udcc2\", width=3, command=self.browse_output_folder).pack(side=LEFT, padx=2)\n        ToolTip(output_entry, \"\u041f\u0430\u043f\u043a\u0430 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\")\n        \n        # \ud83d\udd27 \u0413\u0420\u0423\u041f\u041f\u0410 2: \u041f\u0430\u043d\u0435\u043b\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 (\u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0430\u044f)\n        toolbar_frame = LabelFrame(main_frame, text=\"\ud83d\udd27 \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f\", font=(\"Arial\", 9, \"bold\"))\n        toolbar_frame.pack(fill=X, pady=(0, 2))\n        \n        toolbar = Frame(toolbar_frame)\n        toolbar.pack(fill=X, padx=5, pady=3)\n        \n        # \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f (\u043a\u0440\u0443\u043f\u043d\u044b\u0435 \u043a\u043d\u043e\u043f\u043a\u0438)\n        actions_frame = Frame(toolbar)\n        actions_frame.pack(side=LEFT)\n        \n        self.btn_load = Button(actions_frame, text=\"\ud83d\udce5 \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c\", command=self.load_file, \n                              bg=\"#E8F5E8\", font=(\"Arial\", 9, \"bold\"), width=12)\n        self.btn_load.pack(side=LEFT, padx=2)\n        ToolTip(self.btn_load, \"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c Excel \u0444\u0430\u0439\u043b\")\n        \n        self.btn_process = Button(actions_frame, text=\"\u2699\ufe0f \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c\", command=self.process_table,\n                                 bg=\"#FFF4E6\", font=(\"Arial\", 9, \"bold\"), width=12)\n        self.btn_process.pack(side=LEFT, padx=2)\n        ToolTip(self.btn_process, \"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0438 \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\")\n        \n        self.btn_save = Button(actions_frame, text=\"\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=self.save_results,\n                              bg=\"#E6F3FF\", font=(\"Arial\", 9, \"bold\"), width=12)\n        self.btn_save.pack(side=LEFT, padx=2)\n        ToolTip(self.btn_save, \"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 Excel\")\n        \n        # \ud83c\udf10 \u041a\u041d\u041e\u041f\u041a\u0410 \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u041e\u0412 (\u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430)\n        if self.translation_available:\n            self.btn_translate = Button(actions_frame, text=\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\", command=self.open_translation_dialog,\n                                      bg=\"#F0E6FF\", font=(\"Arial\", 9, \"bold\"), width=12)\n            self.btn_translate.pack(side=LEFT, padx=2)\n            try:\n                ToolTip(self.btn_translate, \"\u0424\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432:\\n\u2022 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\\n\u2022 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\\n\u2022 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\\n\u2022 \u0418\u043c\u043f\u043e\u0440\u0442/\u044d\u043a\u0441\u043f\u043e\u0440\u0442\")\n            except Exception as e:\n                print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f ToolTip \u0434\u043b\u044f \u043a\u043d\u043e\u043f\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {e}\")\n        \n        # \u0411\u044b\u0441\u0442\u0440\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n        # \u0411\u044b\u0441\u0442\u0440\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 (\u043c\u0435\u043b\u043a\u0438\u0435 \u043a\u043d\u043e\u043f\u043a\u0438)\n        try:\n            settings_frame = Frame(toolbar)\n            settings_frame.pack(side=RIGHT)\n            print(\"\ud83d\udd0d DEBUG: settings_frame \u0441\u043e\u0437\u0434\u0430\u043d\")\n        except Exception as e:\n            print(f\"\ud83d\udd0d DEBUG: \u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f settings_frame: {e}\")\n        \n        try:\n            Label(settings_frame, text=\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438:\", font=(\"Arial\", 8)).pack(side=LEFT, padx=(10, 5))\n            print(\"\ud83d\udd0d DEBUG: Label '\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438' \u0441\u043e\u0437\u0434\u0430\u043d\")\n        except Exception as e:\n            print(f\"\ud83d\udd0d DEBUG: \u041e\u0448\u0438\u0431\u043a\u0430 Label '\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438': {e}\")\n        \n        try:\n            self.btn_replacement_rules = Button(settings_frame, text=\"\ud83d\udd04\", width=3, command=self.open_replacement_rules_editor,\n                   bg=\"#FFE6E6\")\n            self.btn_replacement_rules.pack(side=LEFT, padx=1)\n            ToolTip(self.btn_replacement_rules, \"\u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\")\n            print(\"\ud83d\udd0d DEBUG: btn_replacement_rules \u0441\u043e\u0437\u0434\u0430\u043d\u0430\")\n        except Exception as e:\n            print(f\"\ud83d\udd0d DEBUG: \u041e\u0448\u0438\u0431\u043a\u0430 btn_replacement_rules: {e}\")\n        \n        self.btn_case_norm = Button(settings_frame, text=\"Aa\", width=3, command=self.open_case_normalization_dialog,\n               bg=\"#E6FFE6\")\n        self.btn_case_norm.pack(side=LEFT, padx=1)\n        ToolTip(self.btn_case_norm, \"\u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\")\n        \n        self.btn_synonyms = Button(settings_frame, text=\"\u2248\", width=3, command=self.open_value_synonyms_dialog,\n               bg=\"#E6F7FF\")\n        self.btn_synonyms.pack(side=LEFT, padx=1)\n        ToolTip(self.btn_synonyms, \"\u0421\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\")\n        \n        self.btn_protected_values = Button(settings_frame, text=\"\ud83d\udee1\ufe0f\", width=3, command=self.open_protected_values_dialog,\n               bg=\"#FFE6CC\")\n        self.btn_protected_values.pack(side=LEFT, padx=1)\n        ToolTip(self.btn_protected_values, \"\u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\")\n        \n        # \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b\n        if self.settings.get(\"extra_cols\"):\n            col_count = len(self.settings['extra_cols'])\n            self.extra_cols_label.config(text=f\"({col_count})\")\n            self.extra_cols_label.config(bg=\"lightgreen\")\n            ToolTip(self.extra_cols_label, f\"\u0412\u044b\u0431\u0440\u0430\u043d\u043e {col_count} \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\")\n\n        # \ud83d\udcca \u041e\u0421\u041d\u041e\u0412\u041d\u0410\u042f \u0420\u0410\u0411\u041e\u0427\u0410\u042f \u041e\u0411\u041b\u0410\u0421\u0422\u042c (\u0432\u043a\u043b\u0430\u0434\u043a\u0438)\n        self.notebook = ttk.Notebook(main_frame)\n        self.notebook.pack(fill=BOTH, expand=True, pady=2)\n\n        # \u0412\u043a\u043b\u0430\u0434\u043a\u0430 1: \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430\n        self.main_tab = Frame(self.notebook)\n        self.notebook.add(self.main_tab, text=\"\ud83d\udccb \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430\")\n        self.setup_main_tab()\n\n        # \u0412\u043a\u043b\u0430\u0434\u043a\u0430 2: \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        self.params_merge_tab = Frame(self.notebook)\n        self.notebook.add(self.params_merge_tab, text=\"\ud83d\udd04 \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\")\n        self.setup_params_merge_tab()\n\n        # \ud83c\udd95 \u0412\u043a\u043b\u0430\u0434\u043a\u0430 3: \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n        self.values_translation_tab = Frame(self.notebook)\n        self.notebook.add(self.values_translation_tab, text=\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\")\n        self.setup_values_translation_tab()\n\n        # \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u0430\u044f \u043f\u0430\u043d\u0435\u043b\u044c \u0432\u043d\u0438\u0437\u0443 (\u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0430\u044f)\n        self.info_label = Label(main_frame, text=\"\u2705 \u0413\u043e\u0442\u043e\u0432 \u043a \u0440\u0430\u0431\u043e\u0442\u0435\", relief=SUNKEN, anchor=W, \n                               font=(\"Arial\", 8), bg=\"#F0F0F0\")\n        self.info_label.pack(fill=X, pady=(2, 0))\n\n    def create_compact_menu(self):\n        \"\"\"\u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u0441 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u043d\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438\"\"\"\n        menubar = Menu(self.root)\n        self.root.config(menu=menubar)\n        \n        # \u041c\u0435\u043d\u044e \"\u0424\u0430\u0439\u043b\"\n        file_menu = Menu(menubar, tearoff=0)\n        menubar.add_cascade(label=\"\ud83d\udcc1 \u0424\u0430\u0439\u043b\", menu=file_menu)\n        file_menu.add_command(label=\"\ud83d\udce5 \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\", command=self.load_file, accelerator=\"Ctrl+O\")\n        file_menu.add_command(label=\"\u2699\ufe0f \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\", command=self.process_table, accelerator=\"Ctrl+P\")\n        file_menu.add_separator()\n        file_menu.add_command(label=\"\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\", command=self.save_results, accelerator=\"Ctrl+S\")\n        file_menu.add_separator()\n        file_menu.add_command(label=\"\ud83d\udeaa \u0412\u044b\u0445\u043e\u0434\", command=self.on_close, accelerator=\"Ctrl+Q\")\n        \n        # \u041c\u0435\u043d\u044e \"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\" (\u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438)\n        settings_menu = Menu(menubar, tearoff=0)\n        menubar.add_cascade(label=\"\u2699\ufe0f \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\", menu=settings_menu)\n        settings_menu.add_command(label=\"\ud83d\udd04 \u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\", command=self.open_replacement_rules_editor)\n        settings_menu.add_command(label=\"Aa \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\", command=self.open_case_normalization_dialog)\n        settings_menu.add_command(label=\"\u2248 \u0421\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\", command=self.open_value_synonyms_dialog)\n        settings_menu.add_command(label=\"\ud83d\udee1\ufe0f \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", command=self.open_protected_values_dialog)\n        settings_menu.add_separator()\n        settings_menu.add_command(label=\"\ud83d\udccb \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\u043c\u0438\", command=self.open_exclusions_dialog)\n        \n        # \ud83c\udf10 \u041c\u0415\u041d\u042e \"\u041f\u0415\u0420\u0415\u0412\u041e\u0414\u042b\" (\u0435\u0441\u043b\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u0430)\n        print(f\"\ud83d\udd0d DEBUG: \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043c\u0435\u043d\u044e - self.translation_available = {self.translation_available}\")\n        if self.translation_available:\n            print(\"\ud83d\udd0d DEBUG: \u0421\u041e\u0417\u0414\u0410\u0415\u041c \u043c\u0435\u043d\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432!\")\n            translation_menu = Menu(menubar, tearoff=0)\n            menubar.add_cascade(label=\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\", menu=translation_menu)\n            translation_menu.add_command(label=\"\u2699\ufe0f \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", command=self.open_translation_dialog)\n            translation_menu.add_separator()\n            translation_menu.add_command(label=\"\ud83d\udd04 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\", command=self.translate_current_parameters)\n            translation_menu.add_command(label=\"\ud83d\udd04 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", command=self.translate_current_values)\n            translation_menu.add_separator()\n            translation_menu.add_command(label=\"\ud83d\udcbe \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", command=self.export_translations)\n            translation_menu.add_command(label=\"\ud83d\udcc2 \u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", command=self.import_translations)\n            print(\"\ud83d\udd0d DEBUG: \u041c\u0435\u043d\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043e!\")\n        else:\n            print(\"\ud83d\udd0d DEBUG: \u041d\u0415 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0435\u043d\u044e - \u0443\u0441\u043b\u043e\u0432\u0438\u0435 False\")\n        \n        # \u041c\u0435\u043d\u044e \"\u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b\"\n        tools_menu = Menu(menubar, tearoff=0)\n        menubar.add_cascade(label=\"\ud83d\udd27 \u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b\", menu=tools_menu)\n        tools_menu.add_command(label=\"\ud83d\udd0d \u041d\u0430\u0439\u0442\u0438 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\", command=self.detect_similar_parameters)\n        tools_menu.add_command(label=\"\ud83d\udce4 \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432\", command=self.export_mappings)\n        tools_menu.add_command(label=\"\ud83d\udce5 \u0418\u043c\u043f\u043e\u0440\u0442 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432\", command=self.import_mappings)\n        tools_menu.add_separator()\n        tools_menu.add_command(label=\"\ud83e\uddf9 \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\", command=self.normalize_all_values_format)\n        \n        # \u041c\u0435\u043d\u044e \"\u041f\u043e\u043c\u043e\u0449\u044c\"\n        help_menu = Menu(menubar, tearoff=0)\n        menubar.add_cascade(label=\"\u2753 \u041f\u043e\u043c\u043e\u0449\u044c\", menu=help_menu)\n        help_menu.add_command(label=\"\ud83d\udcda \u0421\u043f\u0440\u0430\u0432\u043a\u0430\", command=self.show_help_dialog, accelerator=\"F1\")\n        help_menu.add_separator()\n        help_menu.add_command(label=\"\u2139\ufe0f \u041e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435\", command=self.show_about_dialog)\n        help_menu.add_command(label=\"\u2328\ufe0f \u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438\", command=self.show_hotkeys_dialog)\n\n        # \u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438\n        self.root.bind('<Control-o>', lambda e: self.load_file())\n        self.root.bind('<Control-p>', lambda e: self.process_table())\n        self.root.bind('<Control-s>', lambda e: self.save_results())\n        self.root.bind('<Control-q>', lambda e: self.on_close())\n        self.root.bind('<F1>', lambda e: self.show_help_dialog())\n\n    def setup_main_tab(self):\n        \"\"\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439\"\"\"\n        tree_frame = Frame(self.main_tab)\n        tree_frame.pack(fill=BOTH, expand=True, pady=5)\n\n        vsb = Scrollbar(tree_frame, orient=\"vertical\")\n        vsb.pack(side=RIGHT, fill=Y)\n\n        columns = ('rownum', 'orig_param', 'orig_values', 'new_param', 'delimiter', 'split_result')\n        self.tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=15, yscrollcommand=vsb.set)\n        self.tree.pack(side=LEFT, fill=BOTH, expand=True)\n        vsb.config(command=self.tree.yview)\n\n        self.tree.heading('rownum', text='\u2116')\n        self.tree.heading('orig_param', text='\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440')\n        self.tree.heading('orig_values', text='\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f')\n        self.tree.heading('new_param', text='\u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440')\n        self.tree.heading('delimiter', text='\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c')\n        self.tree.heading('split_result', text='\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f')\n\n        self.tree.column('rownum', width=40, anchor=CENTER)\n        self.tree.column('orig_param', width=130)\n        self.tree.column('orig_values', width=250)\n        self.tree.column('new_param', width=130)\n        self.tree.column('delimiter', width=70)\n        self.tree.column('split_result', width=350)\n\n        self.tree.bind(\"<Double-1>\", self.on_double_click)\n        self.tree.bind(\"<Button-3>\", self.show_context_menu)\n        self.tree.bind(\"<Button-1>\", self.sort_column, True)\n        self.tree.bind(\"<Control-c>\", self.copy_selected_value)\n        self.tree.bind(\"<Control-a>\", self.select_all_rows)\n\n        # \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439\n        mapping_frame = LabelFrame(self.main_tab, text=\"\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u0438 \u043f\u043e\u0438\u0441\u043a\")\n        mapping_frame.pack(fill=X, pady=5)\n\n        search_frame = Frame(mapping_frame)\n        search_frame.pack(fill=X, padx=5, pady=2)\n        Label(search_frame, text=\"\u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c:\").pack(side=LEFT)\n        self.search_var = StringVar()\n        self.search_var.trace_add('write', self.filter_mapping_table)\n        self.search_entry = Entry(search_frame, textvariable=self.search_var)\n        self.search_entry.pack(side=LEFT, fill=X, expand=True, padx=5)\n\n        mapping_vsb = Scrollbar(mapping_frame, orient=\"vertical\")\n        mapping_vsb.pack(side=RIGHT, fill=Y)\n\n        self.mapping_tree = ttk.Treeview(mapping_frame, columns=('original', 'replacement'), show='headings', height=6,\n                                         selectmode='extended',\n                                         yscrollcommand=mapping_vsb.set)\n        self.mapping_tree.heading('original', text='\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435', command=self.sort_mapping_tree_by_original_col)\n        self.mapping_tree.heading('replacement', text='\u0417\u0430\u043c\u0435\u043d\u0430')\n        self.mapping_tree.column('original', width=250)\n        self.mapping_tree.column('replacement', width=250)\n        self.mapping_tree.pack(side=LEFT, fill=BOTH, expand=True, padx=5)\n        mapping_vsb.config(command=self.mapping_tree.yview)\n\n        self.mapping_tree.bind(\"<Double-1>\", self.on_mapping_double_click)\n        self.mapping_tree.bind(\"<Control-a>\", self.select_all_mappings)\n        self.mapping_tree.bind(\"<Delete>\", self.delete_selected_mappings)\n        self.mapping_tree.bind(\"<Control-c>\", self.copy_mapping_selected_value)\n\n        mapping_buttons = Frame(mapping_frame)\n        mapping_buttons.pack(side=LEFT, padx=5, fill=Y)\n\n        btn_add = Button(mapping_buttons, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\", command=self.add_mapping)\n        btn_add.pack(fill=X, pady=2)\n        btn_del = Button(mapping_buttons, text=\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=self.delete_mapping)\n        btn_del.pack(fill=X, pady=2)\n        btn_select_all = Button(mapping_buttons, text=\"\u0412\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u0441\u0435\", command=self.select_all_mappings)\n        btn_select_all.pack(fill=X, pady=2)\n        ToolTip(btn_select_all, \"\u0412\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\")\n        \n        btn_delete_all = Button(mapping_buttons, text=\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435\", command=self.delete_all_mappings)\n        btn_delete_all.pack(fill=X, pady=2)\n        ToolTip(btn_delete_all, \"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b\")\n        \n        btn_import = Button(mapping_buttons, text=\"\u0418\u043c\u043f\u043e\u0440\u0442\", command=self.import_mappings)\n        btn_import.pack(fill=X, pady=2)\n        btn_export = Button(mapping_buttons, text=\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442\", command=self.export_mappings)\n        btn_export.pack(fill=X, pady=2)\n\n        # \u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439\n        exclusions_frame = LabelFrame(self.main_tab, text=\"\u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 (\u0434\u0432\u043e\u0439\u043d\u043e\u0439 \u043a\u043b\u0438\u043a - \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435)\")\n        exclusions_frame.pack(fill=X, pady=5)\n\n        self.exclusions_tree = ttk.Treeview(exclusions_frame, columns=('exclusion',), show='headings', height=3)\n        self.exclusions_tree.heading('exclusion', text='\u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435')\n        self.exclusions_tree.column('exclusion', width=400)\n        self.exclusions_tree.pack(side=LEFT, fill=BOTH, expand=True)\n        self.exclusions_tree.bind(\"<Double-1>\", self.on_exclusion_double_click)\n        self.exclusions_tree.bind(\"<Control-c>\", self.copy_exclusion_selected_value)\n\n        exclusions_buttons = Frame(exclusions_frame)\n        exclusions_buttons.pack(side=LEFT, padx=5, fill=Y)\n\n        btn_add_exclusion = Button(exclusions_buttons, text=\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\", command=self.add_exclusion)\n        btn_add_exclusion.pack(fill=X, pady=2)\n        btn_del_exclusion = Button(exclusions_buttons, text=\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=self.delete_exclusion)\n        btn_del_exclusion.pack(fill=X, pady=2)\n\n        self.update_exclusions_tree()\n\n    def setup_params_merge_tab(self):\n        \"\"\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        # \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\n        info_frame = LabelFrame(self.params_merge_tab, text=\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\")\n        info_frame.pack(fill=X, padx=5, pady=5)\n        \n        info_text = \"\"\"\u042d\u0442\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u0439\u0442\u0438 \u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u043f\u0435\u0447\u0430\u0442\u043e\u043a.\n\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0431\u0443\u0434\u0443\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445.\n\u041f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b.\"\"\"\n        \n        Label(info_frame, text=info_text, justify=LEFT, wraplength=800).pack(padx=10, pady=5)\n\n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        control_frame = Frame(self.params_merge_tab)\n        control_frame.pack(fill=X, padx=5, pady=5)\n\n        self.btn_find_similar_params = Button(control_frame, text=\"\u041d\u0430\u0439\u0442\u0438 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\", \n                                             command=self.find_similar_parameters_for_tab)\n        self.btn_find_similar_params.pack(side=LEFT, padx=5)\n        \n        self.btn_apply_param_merges = Button(control_frame, text=\"\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\", \n                                           command=self.apply_param_merges_from_tab)\n        self.btn_apply_param_merges.pack(side=LEFT, padx=5)\n        \n        self.btn_clear_param_settings = Button(control_frame, text=\"\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\", \n                                              command=self.clear_param_merge_settings)\n        self.btn_clear_param_settings.pack(side=LEFT, padx=15)\n\n        # \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        table_frame = LabelFrame(self.params_merge_tab, text=\"\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\")\n        table_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n\n        tree_frame = Frame(table_frame)\n        tree_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n\n        columns = ('merge', 'representative', 'similar_params', 'frequency')\n        self.params_tree = ttk.Treeview(tree_frame, columns=columns, show='headings')\n        self.params_tree.pack(side=LEFT, fill=BOTH, expand=True)\n\n        params_vsb = Scrollbar(tree_frame, orient=\"vertical\", command=self.params_tree.yview)\n        params_vsb.pack(side=RIGHT, fill=Y)\n        self.params_tree.configure(yscrollcommand=params_vsb.set)\n\n        # \ud83c\udd95 \u0421\u041e\u0420\u0422\u0418\u0420\u041e\u0412\u041a\u0410 \u041f\u041e \u0417\u0410\u0413\u041e\u041b\u041e\u0412\u041a\u0410\u041c: \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438\n        self.params_tree.heading('merge', text='\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c')\n        self.params_tree.heading('representative', text='\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u2195', \n                                 command=lambda: self.sort_params_tree('representative'))\n        self.params_tree.heading('similar_params', text='\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u2195', \n                                 command=lambda: self.sort_params_tree('similar_params'))\n        self.params_tree.heading('frequency', text='\u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u2195', \n                                 command=lambda: self.sort_params_tree('frequency'))\n\n        self.params_tree.column('merge', width=80, anchor=CENTER)\n        self.params_tree.column('representative', width=250, anchor=W)\n        self.params_tree.column('similar_params', width=350, anchor=W)\n        self.params_tree.column('frequency', width=100, anchor=CENTER)\n\n        # \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438\n        self.params_tree_sort_direction = {}  # {column: 'asc'/'desc'}\n        \n        self.params_tree.bind('<Button-1>', self.on_params_tree_click)\n        self.params_tree.bind('<Button-3>', self.show_params_context_menu)  # \ud83c\udd95 \u041f\u041a\u041c \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n\n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0432\u044b\u0431\u043e\u0440\u0430\n        btn_frame = Frame(table_frame)\n        btn_frame.pack(fill=X, padx=5, pady=5)\n\n        Button(btn_frame, text=\"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435\", command=self.select_all_params).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u0421\u043d\u044f\u0442\u044c \u0432\u0441\u0435\", command=self.deselect_all_params).pack(side=LEFT, padx=5)\n\n    def setup_values_merge_tab(self):\n        \"\"\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        # \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\n        info_frame = LabelFrame(self.values_merge_tab, text=\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\")\n        info_frame.pack(fill=X, padx=5, pady=5)\n        \n        info_text = \"\"\"\u042d\u0442\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u0439\u0442\u0438 \u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.\n\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \"Support\"/\"Supported\", \"Yes\"/\"YES\", \"\u041d\u0435\u0442\"/\"No\" \u0438 \u0442.\u0434.\n\u041e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0431\u0443\u0434\u0443\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445.\"\"\"\n        \n        Label(info_frame, text=info_text, justify=LEFT, wraplength=800).pack(padx=10, pady=5)\n\n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        control_frame = Frame(self.values_merge_tab)\n        control_frame.pack(fill=X, padx=5, pady=5)\n\n        self.btn_find_similar_values = Button(control_frame, text=\"\u041d\u0430\u0439\u0442\u0438 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", \n                                            command=self.find_similar_values_for_tab)\n        self.btn_find_similar_values.pack(side=LEFT, padx=5)\n        \n        self.btn_apply_value_merges = Button(control_frame, text=\"\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\", \n                                           command=self.apply_value_merges_from_tab)\n        self.btn_apply_value_merges.pack(side=LEFT, padx=5)\n        \n        self.btn_clear_value_settings = Button(control_frame, text=\"\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\", \n                                              command=self.clear_value_merge_settings)\n        self.btn_clear_value_settings.pack(side=LEFT, padx=15)\n\n        # \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        table_frame = LabelFrame(self.values_merge_tab, text=\"\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\")\n        table_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n\n        tree_frame = Frame(table_frame)\n        tree_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n\n        columns = ('merge', 'parameter', 'representative', 'similar_values', 'frequency')\n        self.values_tree = ttk.Treeview(tree_frame, columns=columns, show='headings')\n        self.values_tree.pack(side=LEFT, fill=BOTH, expand=True)\n\n        values_vsb = Scrollbar(tree_frame, orient=\"vertical\", command=self.values_tree.yview)\n        values_vsb.pack(side=RIGHT, fill=Y)\n        self.values_tree.configure(yscrollcommand=values_vsb.set)\n\n        self.values_tree.heading('merge', text='\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c')\n        self.values_tree.heading('parameter', text='\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440')\n        self.values_tree.heading('representative', text='\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435')\n        self.values_tree.heading('similar_values', text='\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f')\n        self.values_tree.heading('frequency', text='\u0427\u0430\u0441\u0442\u043e\u0442\u0430')\n\n        self.values_tree.column('merge', width=80, anchor=CENTER)\n        self.values_tree.column('parameter', width=200, anchor=W)\n        self.values_tree.column('representative', width=150, anchor=W)\n        self.values_tree.column('similar_values', width=300, anchor=W)\n        self.values_tree.column('frequency', width=100, anchor=CENTER)\n\n        self.values_tree.bind('<Button-1>', self.on_values_tree_click)\n\n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0432\u044b\u0431\u043e\u0440\u0430\n        btn_frame = Frame(table_frame)\n        btn_frame.pack(fill=X, padx=5, pady=5)\n\n        Button(btn_frame, text=\"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435\", command=self.select_all_values).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u0421\u043d\u044f\u0442\u044c \u0432\u0441\u0435\", command=self.deselect_all_values).pack(side=LEFT, padx=5)\n\n    def setup_values_translation_tab(self):\n        \"\"\"\ud83c\udd95 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\"\"\"\n        # \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0435\u0441\u043b\u0438 \u0438\u0445 \u043d\u0435\u0442\n        if not hasattr(self, 'value_translation_rules'):\n            self.value_translation_rules = []\n            self.load_value_translation_rules()\n        \n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c\n        header_frame = LabelFrame(self.values_translation_tab, text=\"\u2139\ufe0f \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", font=(\"Arial\", 10, \"bold\"))\n        header_frame.pack(fill=X, padx=5, pady=5)\n        \n        info_text = \"\"\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n        \n\u042d\u0442\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439:\n\u2022 \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f: mm \u2192 \u043c\u043c, kg \u2192 \u043a\u0433, Wh \u2192 \u0412\u0442\u22c5\u0447\n\u2022 \u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u044b: Support \u2192 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430, Auto \u2192 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438  \n\u2022 \u041b\u044e\u0431\u044b\u0435 \u0434\u0440\u0443\u0433\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: Yes \u2192 \u0414\u0430, No \u2192 \u041d\u0435\u0442\n\n\u041f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c\" \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0439\u0434\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \n\u0438 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430.\"\"\"\n        \n        Label(header_frame, text=info_text, justify=LEFT, wraplength=900, font=(\"Arial\", 9)).pack(padx=10, pady=5)\n        \n        # \u041f\u0430\u043d\u0435\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        control_frame = LabelFrame(self.values_translation_tab, text=\"\ud83d\udd27 \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\", font=(\"Arial\", 10, \"bold\"))\n        control_frame.pack(fill=X, padx=5, pady=5)\n        \n        buttons_frame = Frame(control_frame)\n        buttons_frame.pack(fill=X, padx=5, pady=5)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        Button(buttons_frame, text=\"\u2795 \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u043e\", command=self.add_value_translation_rule,\n               bg=\"#E8F5E8\", font=(\"Arial\", 9)).pack(side=LEFT, padx=5)\n        \n        Button(buttons_frame, text=\"\u270f\ufe0f \u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", command=self.edit_value_translation_rule,\n               bg=\"#FFF4E6\", font=(\"Arial\", 9)).pack(side=LEFT, padx=5)\n        \n        Button(buttons_frame, text=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=self.delete_value_translation_rule,\n               bg=\"#FFE6E6\", font=(\"Arial\", 9)).pack(side=LEFT, padx=5)\n        \n        Button(buttons_frame, text=\"\ud83d\udd0d \u041d\u0430\u0439\u0442\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u044b\", command=self.auto_detect_units,\n               bg=\"#E6F3FF\", font=(\"Arial\", 9, \"bold\")).pack(side=LEFT, padx=15)\n        \n        Button(buttons_frame, text=\"\ud83d\udcbe \u042d\u043a\u0441\u043f\u043e\u0440\u0442\", command=self.export_value_translations,\n               bg=\"#F0E6FF\", font=(\"Arial\", 9)).pack(side=LEFT, padx=5)\n        \n        Button(buttons_frame, text=\"\ud83d\udce5 \u0418\u043c\u043f\u043e\u0440\u0442\", command=self.import_value_translations,\n               bg=\"#F0E6FF\", font=(\"Arial\", 9)).pack(side=LEFT, padx=5)\n        \n        # \u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n        table_frame = LabelFrame(self.values_translation_tab, text=\"\ud83d\udccb \u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\", font=(\"Arial\", 10, \"bold\"))\n        table_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0440\u0435\u0439\u043c \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0431\u0430\u0440\u0430\n        tree_frame = Frame(table_frame)\n        tree_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)\n        \n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043b\u043e\u043d\u043e\u043a\n        columns = ('active', 'original', 'translation', 'category', 'description', 'examples')\n        self.translation_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=15)\n        \n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432\n        self.translation_tree.heading('active', text='\u2713')\n        self.translation_tree.heading('original', text='\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435')  \n        self.translation_tree.heading('translation', text='\u041f\u0435\u0440\u0435\u0432\u043e\u0434')\n        self.translation_tree.heading('category', text='\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f')\n        self.translation_tree.heading('description', text='\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435')\n        self.translation_tree.heading('examples', text='\u041f\u0440\u0438\u043c\u0435\u0440\u044b')\n        \n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u044b \u043a\u043e\u043b\u043e\u043d\u043e\u043a\n        self.translation_tree.column('active', width=30, anchor=CENTER)\n        self.translation_tree.column('original', width=150, anchor=W)\n        self.translation_tree.column('translation', width=150, anchor=W)\n        self.translation_tree.column('category', width=100, anchor=W)\n        self.translation_tree.column('description', width=200, anchor=W)\n        self.translation_tree.column('examples', width=200, anchor=W)\n        \n        # \u0421\u043a\u0440\u043e\u043b\u043b\u0431\u0430\u0440\u044b\n        v_scrollbar = Scrollbar(tree_frame, orient=VERTICAL, command=self.translation_tree.yview)\n        h_scrollbar = Scrollbar(tree_frame, orient=HORIZONTAL, command=self.translation_tree.xview)\n        self.translation_tree.configure(yscrollcommand=v_scrollbar.set, xscrollcommand=h_scrollbar.set)\n        \n        # \u0420\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432\n        self.translation_tree.pack(side=LEFT, fill=BOTH, expand=True)\n        v_scrollbar.pack(side=RIGHT, fill=Y)\n        h_scrollbar.pack(side=BOTTOM, fill=X)\n        \n        # \u041f\u0440\u0438\u0432\u044f\u0437\u043a\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 v4.8\n        self.translation_tree.bind('<Double-1>', lambda e: self.edit_value_translation_rule())\n        self.translation_tree.bind('<Button-3>', self.show_translation_context_menu)\n        self.translation_tree.bind('<Button-1>', self.on_translation_tree_click)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0432\u044b\u0431\u043e\u0440\u0430 v4.8\n        selection_frame = Frame(table_frame)\n        selection_frame.pack(fill=X, padx=5, pady=5)\n        \n        # \ud83c\udd95 \u041c\u0430\u0441\u0441\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 v4.8\n        Button(selection_frame, text=\"\u2713 \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435\", command=self.activate_selected_rules,\n               bg=\"#E8F5E8\", font=(\"Arial\", 9)).pack(side=LEFT, padx=2)\n        Button(selection_frame, text=\"\u2717 \u0414\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435\", command=self.deactivate_selected_rules,\n               bg=\"#FFE6E6\", font=(\"Arial\", 9)).pack(side=LEFT, padx=2)\n        \n        # \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c\n        Label(selection_frame, text=\" | \", font=(\"Arial\", 9)).pack(side=LEFT, padx=5)\n        \n        Button(selection_frame, text=\"\u2713 \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435\", command=self.activate_all_translation_rules,\n               font=(\"Arial\", 8)).pack(side=LEFT, padx=2)\n        Button(selection_frame, text=\"\u2717 \u0414\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435\", command=self.deactivate_all_translation_rules,\n               font=(\"Arial\", 8)).pack(side=LEFT, padx=2)\n        \n        # \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 v4.8\n        stats_right = Frame(selection_frame)\n        stats_right.pack(side=RIGHT, padx=5)\n        \n        self.translation_stats_label = Label(stats_right, text=\"\u041f\u0440\u0430\u0432\u0438\u043b: 0 | \u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0445: 0\", \n                                           font=(\"Arial\", 9), fg=\"blue\")\n        self.translation_stats_label.pack(side=TOP)\n        \n        self.selected_count_label = Label(stats_right, text=\"\u0412\u044b\u0431\u0440\u0430\u043d\u043e: 0\", \n                                        font=(\"Arial\", 9), fg=\"green\")\n        self.selected_count_label.pack(side=TOP)\n        \n        # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n        self.refresh_translation_rules_display()\n\n    def load_value_translation_rules(self):\n        \"\"\"\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430\"\"\"\n        try:\n            if os.path.exists(\"value_translation_rules.json\"):\n                with open(\"value_translation_rules.json\", \"r\", encoding=\"utf-8\") as f:\n                    self.value_translation_rules = json.load(f)\n                print(f\"\u2705 \u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043e {len(self.value_translation_rules)} \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\")\n            else:\n                # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e\n                self.value_translation_rules = self.get_default_translation_rules()\n                self.save_value_translation_rules()\n                print(f\"\u2705 \u0421\u043e\u0437\u0434\u0430\u043d\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e: {len(self.value_translation_rules)} \u0437\u0430\u043f\u0438\u0441\u0435\u0439\")\n        except Exception as e:\n            print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430: {e}\")\n            self.value_translation_rules = self.get_default_translation_rules()\n\n    def get_default_translation_rules(self):\n        \"\"\"\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u0430\u0431\u043e\u0440 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e\"\"\"\n        return [\n            # \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f - \u0434\u043b\u0438\u043d\u0430\n            {\"active\": True, \"original\": \"mm\", \"translation\": \"\u043c\u043c\", \"category\": \"\u0414\u043b\u0438\u043d\u0430\", \n             \"description\": \"\u041c\u0438\u043b\u043b\u0438\u043c\u0435\u0442\u0440\u044b\", \"examples\": \"5mm \u2192 5 \u043c\u043c\"},\n            {\"active\": True, \"original\": \"cm\", \"translation\": \"\u0441\u043c\", \"category\": \"\u0414\u043b\u0438\u043d\u0430\",\n             \"description\": \"\u0421\u0430\u043d\u0442\u0438\u043c\u0435\u0442\u0440\u044b\", \"examples\": \"10cm \u2192 10 \u0441\u043c\"},\n            {\"active\": True, \"original\": \"m\", \"translation\": \"\u043c\", \"category\": \"\u0414\u043b\u0438\u043d\u0430\",\n             \"description\": \"\u041c\u0435\u0442\u0440\u044b\", \"examples\": \"1.5m \u2192 1.5 \u043c\"},\n            {\"active\": True, \"original\": \"km\", \"translation\": \"\u043a\u043c\", \"category\": \"\u0414\u043b\u0438\u043d\u0430\",\n             \"description\": \"\u041a\u0438\u043b\u043e\u043c\u0435\u0442\u0440\u044b\", \"examples\": \"5km \u2192 5 \u043a\u043c\"},\n            {\"active\": True, \"original\": \"in\", \"translation\": \"\u0434\u044e\u0439\u043c\", \"category\": \"\u0414\u043b\u0438\u043d\u0430\",\n             \"description\": \"\u0414\u044e\u0439\u043c\u044b\", \"examples\": '24in \u2192 24\"'},\n            \n            # \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f - \u0432\u0435\u0441\n            {\"active\": True, \"original\": \"mg\", \"translation\": \"\u043c\u0433\", \"category\": \"\u0412\u0435\u0441\",\n             \"description\": \"\u041c\u0438\u043b\u043b\u0438\u0433\u0440\u0430\u043c\u043c\u044b\", \"examples\": \"500mg \u2192 500 \u043c\u0433\"},\n            {\"active\": True, \"original\": \"g\", \"translation\": \"\u0433\", \"category\": \"\u0412\u0435\u0441\",\n             \"description\": \"\u0413\u0440\u0430\u043c\u043c\u044b\", \"examples\": \"250g \u2192 250 \u0433\"},\n            {\"active\": True, \"original\": \"kg\", \"translation\": \"\u043a\u0433\", \"category\": \"\u0412\u0435\u0441\",\n             \"description\": \"\u041a\u0438\u043b\u043e\u0433\u0440\u0430\u043c\u043c\u044b\", \"examples\": \"1.5kg \u2192 1.5 \u043a\u0433\"},\n            \n            # \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f - \u044d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\n            {\"active\": True, \"original\": \"V\", \"translation\": \"\u0412\", \"category\": \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\",\n             \"description\": \"\u0412\u043e\u043b\u044c\u0442\u044b\", \"examples\": \"220V \u2192 220 \u0412\"},\n            {\"active\": True, \"original\": \"A\", \"translation\": \"\u0410\", \"category\": \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\",\n             \"description\": \"\u0410\u043c\u043f\u0435\u0440\u044b\", \"examples\": \"2A \u2192 2 \u0410\"},\n            {\"active\": True, \"original\": \"W\", \"translation\": \"\u0412\u0442\", \"category\": \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\",\n             \"description\": \"\u0412\u0430\u0442\u0442\u044b\", \"examples\": \"100W \u2192 100 \u0412\u0442\"},\n            {\"active\": True, \"original\": \"Wh\", \"translation\": \"\u0412\u0442\u22c5\u0447\", \"category\": \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\",\n             \"description\": \"\u0412\u0430\u0442\u0442-\u0447\u0430\u0441\u044b\", \"examples\": \"5000Wh \u2192 5000 \u0412\u0442\u22c5\u0447\"},\n            {\"active\": True, \"original\": \"kWh\", \"translation\": \"\u043a\u0412\u0442\u22c5\u0447\", \"category\": \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\",\n             \"description\": \"\u041a\u0438\u043b\u043e\u0432\u0430\u0442\u0442-\u0447\u0430\u0441\u044b\", \"examples\": \"10kWh \u2192 10 \u043a\u0412\u0442\u22c5\u0447\"},\n            \n            # \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f - \u0447\u0430\u0441\u0442\u043e\u0442\u0430\n            {\"active\": True, \"original\": \"Hz\", \"translation\": \"\u0413\u0446\", \"category\": \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430\",\n             \"description\": \"\u0413\u0435\u0440\u0446\u044b\", \"examples\": \"50Hz \u2192 50 \u0413\u0446\"},\n            {\"active\": True, \"original\": \"kHz\", \"translation\": \"\u043a\u0413\u0446\", \"category\": \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430\",\n             \"description\": \"\u041a\u0438\u043b\u043e\u0433\u0435\u0440\u0446\u044b\", \"examples\": \"20kHz \u2192 20 \u043a\u0413\u0446\"},\n            {\"active\": True, \"original\": \"MHz\", \"translation\": \"\u041c\u0413\u0446\", \"category\": \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430\",\n             \"description\": \"\u041c\u0435\u0433\u0430\u0433\u0435\u0440\u0446\u044b\", \"examples\": \"2.4MHz \u2192 2.4 \u041c\u0413\u0446\"},\n            {\"active\": True, \"original\": \"GHz\", \"translation\": \"\u0413\u0413\u0446\", \"category\": \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430\",\n             \"description\": \"\u0413\u0438\u0433\u0430\u0433\u0435\u0440\u0446\u044b\", \"examples\": \"2.4GHz \u2192 2.4 \u0413\u0413\u0446\"},\n            \n            # \ud83c\udd95 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n            {\"active\": True, \"original\": \"bps\", \"translation\": \"\u0431\u0438\u0442/\u0441\", \"category\": \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u0411\u0438\u0442\u044b \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443\", \"examples\": \"1000bps \u2192 1000 \u0431\u0438\u0442/\u0441\"},\n            {\"active\": True, \"original\": \"kbps\", \"translation\": \"\u043a\u0431\u0438\u0442/\u0441\", \"category\": \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u041a\u0438\u043b\u043e\u0431\u0438\u0442\u044b \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443\", \"examples\": \"32kbps \u2192 32 \u043a\u0431\u0438\u0442/\u0441\"},\n            {\"active\": True, \"original\": \"Mbps\", \"translation\": \"\u041c\u0431\u0438\u0442/\u0441\", \"category\": \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u041c\u0435\u0433\u0430\u0431\u0438\u0442\u044b \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443\", \"examples\": \"16Mbps \u2192 16 \u041c\u0431\u0438\u0442/\u0441\"},\n            {\"active\": True, \"original\": \"Gbps\", \"translation\": \"\u0413\u0431\u0438\u0442/\u0441\", \"category\": \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u0413\u0438\u0433\u0430\u0431\u0438\u0442\u044b \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443\", \"examples\": \"1Gbps \u2192 1 \u0413\u0431\u0438\u0442/\u0441\"},\n            {\"active\": True, \"original\": \"fps\", \"translation\": \"\u043a/\u0441\", \"category\": \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u041a\u0430\u0434\u0440\u044b \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443\", \"examples\": \"30fps \u2192 30 \u043a/\u0441\"},\n            \n            # \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f - \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\n            {\"active\": True, \"original\": \"lux\", \"translation\": \"\u043b\u043a\", \"category\": \"\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u041b\u044e\u043a\u0441\u044b\", \"examples\": \"500lux \u2192 500 \u043b\u043a\"},\n            {\"active\": True, \"original\": \"lm\", \"translation\": \"\u043b\u043c\", \"category\": \"\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\",\n             \"description\": \"\u041b\u044e\u043c\u0435\u043d\u044b\", \"examples\": \"800lm \u2192 800 \u043b\u043c\"},\n            \n            # \u0422\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0442\u0435\u0440\u043c\u0438\u043d\u044b\n            {\"active\": True, \"original\": \"Support\", \"translation\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430\", \"category\": \"\u0422\u0435\u0440\u043c\u0438\u043d\u044b\",\n             \"description\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\", \"examples\": \"Support \u2192 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430\"},\n            {\"active\": True, \"original\": \"Supported\", \"translation\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\", \"category\": \"\u0422\u0435\u0440\u043c\u0438\u043d\u044b\",\n             \"description\": \"\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\", \"examples\": \"Supported \u2192 \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f\"},\n            {\"active\": True, \"original\": \"Auto\", \"translation\": \"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\", \"category\": \"\u0422\u0435\u0440\u043c\u0438\u043d\u044b\",\n             \"description\": \"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0440\u0435\u0436\u0438\u043c\", \"examples\": \"Auto \u2192 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\"},\n            {\"active\": True, \"original\": \"Manual\", \"translation\": \"\u0412\u0440\u0443\u0447\u043d\u0443\u044e\", \"category\": \"\u0422\u0435\u0440\u043c\u0438\u043d\u044b\",\n             \"description\": \"\u0420\u0443\u0447\u043d\u043e\u0439 \u0440\u0435\u0436\u0438\u043c\", \"examples\": \"Manual \u2192 \u0412\u0440\u0443\u0447\u043d\u0443\u044e\"},\n            \n            # \u0414\u0430/\u041d\u0435\u0442\n            {\"active\": True, \"original\": \"Yes\", \"translation\": \"\u0414\u0430\", \"category\": \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435\",\n             \"description\": \"\u0423\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442\", \"examples\": \"Yes \u2192 \u0414\u0430\"},\n            {\"active\": True, \"original\": \"No\", \"translation\": \"\u041d\u0435\u0442\", \"category\": \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435\",\n             \"description\": \"\u041e\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442\", \"examples\": \"No \u2192 \u041d\u0435\u0442\"},\n            {\"active\": True, \"original\": \"True\", \"translation\": \"\u0418\u0441\u0442\u0438\u043d\u0430\", \"category\": \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435\",\n             \"description\": \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0438\u0441\u0442\u0438\u043d\u0430\", \"examples\": \"True \u2192 \u0418\u0441\u0442\u0438\u043d\u0430\"},\n            {\"active\": True, \"original\": \"False\", \"translation\": \"\u041b\u043e\u0436\u044c\", \"category\": \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435\",\n             \"description\": \"\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043b\u043e\u0436\u044c\", \"examples\": \"False \u2192 \u041b\u043e\u0436\u044c\"},\n        ]\n\n    def save_value_translation_rules(self):\n        \"\"\"\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0444\u0430\u0439\u043b\"\"\"\n        try:\n            with open(\"value_translation_rules.json\", \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.value_translation_rules, f, ensure_ascii=False, indent=2)\n            print(f\"\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043e {len(self.value_translation_rules)} \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\")\n            \n            # \ud83c\udd95 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b v4.8\n            self.dynamic_update_main_table()\n            \n        except Exception as e:\n            print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430: {e}\")\n    \n    def dynamic_update_main_table(self):\n        \"\"\"\ud83c\udd95 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u0440\u0430\u0432\u0438\u043b v4.8\"\"\"\n        try:\n            if not hasattr(self, 'tree') or not self.tree.get_children():\n                print(\"\u2139\ufe0f \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u0443\u0441\u0442\u0430 - \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f\")\n                return\n            \n            print(\"\ud83d\udd04 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b...\")\n            \n            updated_count = 0\n            \n            # \u041f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0432\u0441\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0430\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b\n            for item in self.tree.get_children():\n                values = self.tree.item(item, \"values\")\n                if len(values) >= 6:\n                    # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u0438\u043d\u0434\u0435\u043a\u0441 2)\n                    original_values = values[2]\n                    delimiter = values[4] if values[4] else \";\"\n                    \n                    # \ud83c\udd95 \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u042b\u0419 \u043f\u0435\u0440\u0435\u0441\u0447\u0435\u0442 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\" v4.8.2\n                    try:\n                        if not original_values:\n                            new_split_result = \"\"\n                        else:\n                            print(f\"    \ud83d\udd04 \u041f\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c: '{original_values}' (\u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c: '{delimiter}')\")\n                            \n                            # \u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044e\n                            parts = [p.strip() for p in original_values.split(delimiter) if p.strip()]\n                            print(f\"    \ud83d\udccb \u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438: {parts}\")\n                            \n                            # \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u043e\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432\n                            clean_parts = []\n                            for part in parts:\n                                if part and part not in [\"/\", \";\", \",\", \"-\"]:\n                                    clean_parts.append(part)\n                            print(f\"    \ud83d\udccb \u041e\u0447\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438: {clean_parts}\")\n                            \n                            # \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0430\n                            unique_parts = []\n                            seen = set()\n                            for part in clean_parts:\n                                if part.lower() not in seen:\n                                    seen.add(part.lower())\n                                    unique_parts.append(part)\n                            print(f\"    \ud83d\udccb \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438: {unique_parts}\")\n                            \n                            # \ud83c\udd95 \u041e\u0411\u042f\u0417\u0410\u0422\u0415\u041b\u042c\u041d\u041e\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 v4.8.2\n                            if hasattr(self, 'value_translation_rules') and self.value_translation_rules:\n                                try:\n                                    print(f\"    \ud83c\udf10 \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043a: {unique_parts}\")\n                                    translated_parts = self.apply_value_translation_rules(unique_parts)\n                                    print(f\"    \ud83c\udf10 \u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {translated_parts}\")\n                                    unique_parts = translated_parts\n                                except Exception as translate_error:\n                                    print(f\"    \u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {translate_error}\")\n                            \n                            new_split_result = \"; \".join(unique_parts)\n                            print(f\"    \u2705 \u041d\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{new_split_result}'\")\n                            \n                    except Exception as e:\n                        print(f\"  \u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0435\u0440\u0435\u0441\u0447\u0435\u0442\u0430 \u0434\u043b\u044f '{original_values}': {e}\")\n                        new_split_result = values[5]  # \u041e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u0430\u043a \u0431\u044b\u043b\u043e\n                    \n                    # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\n                    if new_split_result != values[5]:\n                        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\n                        new_values = list(values)\n                        new_values[5] = new_split_result\n                        self.tree.item(item, values=new_values)\n                        updated_count += 1\n                        print(f\"  \ud83d\udd04 \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e: '{values[5]}' \u2192 '{new_split_result}'\")\n            \n            if updated_count > 0:\n                print(f\"\u2705 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415: \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e {updated_count} \u0441\u0442\u0440\u043e\u043a\")\n                if hasattr(self, 'info_label'):\n                    self.info_label.config(text=f\"\u2705 \u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e {updated_count} \u0441\u0442\u0440\u043e\u043a\")\n            else:\n                print(\"\u2139\ufe0f \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415: \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f\")\n                \n        except Exception as e:\n            print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f: {e}\")\n\n    def is_protected_abbreviation(self, text):\n        \"\"\"\ud83c\udd95 \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043b\u0438 \u0442\u0435\u043a\u0441\u0442 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0439 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u043e\u0439 v4.8\"\"\"\n        if not text:\n            return False\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u043e\u0447\u043d\u043e\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435\n        if text in PROTECTED_ABBREVIATIONS:\n            return True\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n        text_upper = text.upper()\n        if text_upper in PROTECTED_ABBREVIATIONS:\n            return True\n        \n        return False\n\n    def load_ip_surveillance_units(self):\n        \"\"\"\ud83c\udd95 \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f\"\"\"\n        try:\n            # \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u0437 data/ \u043f\u0430\u043f\u043a\u0438\n            units_files = [\n                \"data/ip_surveillance_units_compact.json\",\n                \"data/ip_surveillance_units_editor.json\",\n                \"ip_surveillance_units_compact.json\",\n                \"ip_surveillance_units_editor.json\"\n            ]\n            \n            loaded = False\n            for units_file in units_files:\n                if os.path.exists(units_file):\n                    with open(units_file, \"r\", encoding=\"utf-8\") as f:\n                        self.ip_surveillance_units = json.load(f)\n                    print(f\"\u2705 \u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0431\u0430\u0437\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439 \u0438\u0437 {units_file}\")\n                    \n                    # \u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n                    self.integrate_ip_surveillance_units()\n                    loaded = True\n                    break\n            \n            if not loaded:\n                print(\"\u2139\ufe0f \u0424\u0430\u0439\u043b\u044b \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b\")\n                print(\"   \u0411\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439\")\n                self.ip_surveillance_units = {}\n                \n        except Exception as e:\n            print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f: {e}\")\n            self.ip_surveillance_units = {}\n\n    def integrate_ip_surveillance_units(self):\n        \"\"\"\ud83c\udd95 \u0418\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u0443\u0435\u0442 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u0439 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n        try:\n            if not self.ip_surveillance_units:\n                return\n            \n            added_count = 0\n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n            existing_originals = {rule.get('original', '').lower() for rule in self.value_translation_rules}\n            \n            # \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437 \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u043e\u0433\u043e \u0444\u043e\u0440\u043c\u0430\u0442\u0430\n            if 'units' in self.ip_surveillance_units:\n                for original, translation in self.ip_surveillance_units['units'].items():\n                    if original.lower() not in existing_originals:\n                        # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b\n                        category = self.determine_unit_category(original)\n                        \n                        new_rule = {\n                            \"active\": True,\n                            \"original\": original,\n                            \"translation\": translation,\n                            \"category\": category,\n                            \"description\": f\"\u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435: {original}\",\n                            \"examples\": f\"{original} \u2192 {translation}\"\n                        }\n                        self.value_translation_rules.append(new_rule)\n                        existing_originals.add(original.lower())\n                        added_count += 1\n            \n            # \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437 \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0444\u043e\u0440\u043c\u0430\u0442\u0430\n            elif 'unit_variants' in self.ip_surveillance_units:\n                for unit, data in self.ip_surveillance_units['unit_variants'].items():\n                    if unit.lower() not in existing_originals:\n                        new_rule = {\n                            \"active\": True,\n                            \"original\": unit,\n                            \"translation\": data.get('russian', unit),\n                            \"category\": data.get('category', 'IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435'),\n                            \"description\": f\"\u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434: {data.get('unit_type', 'general')}\",\n                            \"examples\": f\"{unit} \u2192 {data.get('russian', unit)}\"\n                        }\n                        self.value_translation_rules.append(new_rule)\n                        existing_originals.add(unit.lower())\n                        added_count += 1\n            \n            if added_count > 0:\n                print(f\"\ud83c\udf10 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e {added_count} \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f\")\n                self.save_value_translation_rules()\n            else:\n                print(\"\u2139\ufe0f \u0412\u0441\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0445 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\")\n                \n        except Exception as e:\n            print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0435\u0434\u0438\u043d\u0438\u0446 IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f: {e}\")\n\n    def determine_unit_category(self, unit):\n        \"\"\"\ud83c\udd95 \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044e \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\"\"\"\n        unit_lower = unit.lower()\n        \n        # \u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\n        if any(x in unit_lower for x in ['v', 'volt', 'a', 'amp', 'w', 'watt', 'wh', 'ohm', 'ma', 'mv', 'kw', 'kv']):\n            return \"\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\"\n        \n        # \u0414\u0430\u043d\u043d\u044b\u0435 \u0438 \u043f\u0430\u043c\u044f\u0442\u044c\n        if any(x in unit_lower for x in ['b', 'byte', 'kb', 'mb', 'gb', 'tb', 'pb', 'bit']):\n            return \"\u0414\u0430\u043d\u043d\u044b\u0435\"\n        \n        # \u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438\n        if any(x in unit_lower for x in ['bps', 'kbps', 'mbps', 'gbps']):\n            return \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c\"\n        \n        # \u0412\u0440\u0435\u043c\u044f\n        if any(x in unit_lower for x in ['s', 'sec', 'ms', '\u03bcs', 'ns', 'min', 'h', 'hour', 'day']):\n            return \"\u0412\u0440\u0435\u043c\u044f\"\n        \n        # \u0427\u0430\u0441\u0442\u043e\u0442\u0430\n        if any(x in unit_lower for x in ['hz', 'khz', 'mhz', 'ghz', 'fps']):\n            return \"\u0427\u0430\u0441\u0442\u043e\u0442\u0430\"\n        \n        # \u0420\u0430\u0437\u043c\u0435\u0440\u044b\n        if any(x in unit_lower for x in ['mm', 'cm', 'm', 'km', 'in', 'ft', '\u03bcm', 'nm']):\n            return \"\u0414\u043b\u0438\u043d\u0430\"\n        \n        # \u0412\u0435\u0441\n        if any(x in unit_lower for x in ['g', 'kg', 'mg', 'lb', 'oz']):\n            return \"\u0412\u0435\u0441\"\n        \n        # \u0417\u0432\u0443\u043a\n        if any(x in unit_lower for x in ['db', 'dba', 'dbc', 'dbi', 'dbm']):\n            return \"\u0417\u0432\u0443\u043a\"\n        \n        # \u041e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435\n        if any(x in unit_lower for x in ['lux', 'lm', 'cd', 'nit']):\n            return \"\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\"\n        \n        # \u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430\n        if any(x in unit_lower for x in ['\u00b0c', '\u00b0f', 'c', 'k', 'kelvin', 'celsius']):\n            return \"\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430\"\n        \n        # \u0418\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\n        if any(x in unit_lower for x in ['mp', 'px', 'pixel', 'dpi', 'ppi', 'megapixel']):\n            return \"\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435\"\n        \n        # \u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435\n        if any(x in unit_lower for x in ['pa', 'kpa', 'mpa', 'bar', 'psi', 'atm', 'mmhg']):\n            return \"\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435\"\n        \n        # \u0423\u0433\u043b\u044b\n        if any(x in unit_lower for x in ['\u00b0', 'degree', 'fov', 'aov']):\n            return \"\u0423\u0433\u043e\u043b\"\n        \n        return \"IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u0435\"\n\n    def refresh_translation_rules_display(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\"\"\"\n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        for item in self.translation_tree.get_children():\n            self.translation_tree.delete(item)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        for rule in self.value_translation_rules:\n            active_text = \"\u2713\" if rule.get(\"active\", True) else \"\u2717\"\n            \n            self.translation_tree.insert(\"\", \"end\", values=(\n                active_text,\n                rule.get(\"original\", \"\"),\n                rule.get(\"translation\", \"\"),\n                rule.get(\"category\", \"\"),\n                rule.get(\"description\", \"\"),\n                rule.get(\"examples\", \"\")\n            ))\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443\n        total_rules = len(self.value_translation_rules)\n        active_rules = sum(1 for rule in self.value_translation_rules if rule.get(\"active\", True))\n        self.translation_stats_label.config(text=f\"\u041f\u0440\u0430\u0432\u0438\u043b: {total_rules} | \u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0445: {active_rules}\")\n\n    def add_value_translation_rule(self):\n        \"\"\"\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        dialog = ValueTranslationRuleDialog(self.root, None)\n        if dialog.result:\n            self.value_translation_rules.append(dialog.result)\n            self.save_value_translation_rules()\n            self.refresh_translation_rules_display()\n\n    def edit_value_translation_rule(self):\n        \"\"\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0443\u0435\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        selection = self.translation_tree.selection()\n        if not selection:\n            messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\")\n            return\n        \n        # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\n        item = selection[0]\n        index = self.translation_tree.index(item)\n        \n        if 0 <= index < len(self.value_translation_rules):\n            rule = self.value_translation_rules[index]\n            dialog = ValueTranslationRuleDialog(self.root, rule)\n            if dialog.result:\n                self.value_translation_rules[index] = dialog.result\n                self.save_value_translation_rules()\n                self.refresh_translation_rules_display()\n\n    def delete_value_translation_rule(self):\n        \"\"\"\u0423\u0434\u0430\u043b\u044f\u0435\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        selection = self.translation_tree.selection()\n        if not selection:\n            messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\")\n            return\n        \n        item = selection[0]\n        index = self.translation_tree.index(item)\n        \n        if 0 <= index < len(self.value_translation_rules):\n            rule = self.value_translation_rules[index]\n            if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \n                                 f\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430?\\n\\n'{rule.get('original', '')}' \u2192 '{rule.get('translation', '')}'\"):\n                del self.value_translation_rules[index]\n                self.save_value_translation_rules()\n                self.refresh_translation_rules_display()\n\n    def auto_detect_units(self):\n        \"\"\"\ud83d\udd0d \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432 \u0434\u0430\u043d\u043d\u044b\u0445\"\"\"\n        if not hasattr(self, 'tree') or not self.tree.get_children():\n            messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435\")\n            return\n        \n        # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\n        all_values = []\n        for item in self.tree.get_children():\n            values = self.tree.item(item, 'values')\n            if len(values) >= 6:\n                split_results = values[5]\n                if split_results:\n                    split_values = [v.strip() for v in re.split(r'[;,|]', split_results) if v.strip()]\n                    all_values.extend(split_values)\n        \n        if not all_values:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430\")\n            return\n        \n        # \u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f\n        unit_patterns = {\n            # === \u0424\u0418\u0417\u0418\u0427\u0415\u0421\u041a\u0418\u0415 \u0420\u0410\u0417\u041c\u0415\u0420\u042b ===\n            r'\\b\\d+\\.?\\d*\\s*mm\\b': ('mm', '\u043c\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*cm\\b': ('cm', '\u0441\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*m\\b': ('m', '\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*km\\b': ('km', '\u043a\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*in\\b': ('in', '\u0434\u044e\u0439\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*ft\\b': ('ft', '\u0444\u0443\u0442', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*\u03bcm\\b': ('\u03bcm', '\u043c\u043a\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*nm\\b': ('nm', '\u043d\u043c', '\u0414\u043b\u0438\u043d\u0430'),\n            \n            # === \u0412\u0415\u0421 ===\n            r'\\b\\d+\\.?\\d*\\s*kg\\b': ('kg', '\u043a\u0433', '\u0412\u0435\u0441'),\n            r'\\b\\d+\\.?\\d*\\s*g\\b': ('g', '\u0433', '\u0412\u0435\u0441'),\n            r'\\b\\d+\\.?\\d*\\s*lb\\b': ('lb', '\u0444\u0443\u043d\u0442', '\u0412\u0435\u0441'),\n            r'\\b\\d+\\.?\\d*\\s*oz\\b': ('oz', '\u0443\u043d\u0446\u0438\u044f', '\u0412\u0435\u0441'),\n            \n            # === \u042d\u041b\u0415\u041a\u0422\u0420\u0418\u0427\u0415\u0421\u0422\u0412\u041e ===\n            r'\\b\\d+\\.?\\d*\\s*V\\b': ('V', '\u0412', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*mV\\b': ('mV', '\u043c\u0412', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*kV\\b': ('kV', '\u043a\u0412', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*W\\b': ('W', '\u0412\u0442', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*mW\\b': ('mW', '\u043c\u0412\u0442', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*kW\\b': ('kW', '\u043a\u0412\u0442', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*Wh\\b': ('Wh', '\u0412\u0442\u22c5\u0447', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*kWh\\b': ('kWh', '\u043a\u0412\u0442\u22c5\u0447', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*A\\b': ('A', '\u0410', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*mA\\b': ('mA', '\u043c\u0410', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*\u03bcA\\b': ('\u03bcA', '\u043c\u043a\u0410', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*mAh\\b': ('mAh', '\u043c\u0410\u0447', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*Ohm\\b': ('Ohm', '\u041e\u043c', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*kOhm\\b': ('kOhm', '\u043a\u041e\u043c', '\u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e'),\n            \n            # === \u0427\u0410\u0421\u0422\u041e\u0422\u0410 \u0418 \u0412\u0420\u0415\u041c\u042f ===\n            r'\\b\\d+\\.?\\d*\\s*Hz\\b': ('Hz', '\u0413\u0446', '\u0427\u0430\u0441\u0442\u043e\u0442\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*kHz\\b': ('kHz', '\u043a\u0413\u0446', '\u0427\u0430\u0441\u0442\u043e\u0442\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*MHz\\b': ('MHz', '\u041c\u0413\u0446', '\u0427\u0430\u0441\u0442\u043e\u0442\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*GHz\\b': ('GHz', '\u0413\u0413\u0446', '\u0427\u0430\u0441\u0442\u043e\u0442\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*fps\\b': ('fps', '\u043a\u0430\u0434\u0440/\u0441', '\u0412\u0438\u0434\u0435\u043e'),\n            r'\\b\\d+\\.?\\d*\\s*s\\b': ('s', '\u0441', '\u0412\u0440\u0435\u043c\u044f'),\n            r'\\b\\d+\\.?\\d*\\s*ms\\b': ('ms', '\u043c\u0441', '\u0412\u0440\u0435\u043c\u044f'),\n            r'\\b\\d+\\.?\\d*\\s*\u03bcs\\b': ('\u03bcs', '\u043c\u043a\u0441', '\u0412\u0440\u0435\u043c\u044f'),\n            r'\\b\\d+\\.?\\d*\\s*ns\\b': ('ns', '\u043d\u0441', '\u0412\u0440\u0435\u043c\u044f'),\n            r'\\b\\d+\\.?\\d*\\s*min\\b': ('min', '\u043c\u0438\u043d', '\u0412\u0440\u0435\u043c\u044f'),\n            r'\\b\\d+\\.?\\d*\\s*h\\b': ('h', '\u0447', '\u0412\u0440\u0435\u043c\u044f'),\n            \n            # === \u041f\u0410\u041c\u042f\u0422\u042c \u0418 \u0414\u0410\u041d\u041d\u042b\u0415 ===\n            r'\\b\\d+\\.?\\d*\\s*B\\b': ('B', '\u0411', '\u0414\u0430\u043d\u043d\u044b\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*KB\\b': ('KB', '\u041a\u0411', '\u0414\u0430\u043d\u043d\u044b\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*MB\\b': ('MB', '\u041c\u0411', '\u0414\u0430\u043d\u043d\u044b\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*GB\\b': ('GB', '\u0413\u0411', '\u0414\u0430\u043d\u043d\u044b\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*TB\\b': ('TB', '\u0422\u0411', '\u0414\u0430\u043d\u043d\u044b\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*PB\\b': ('PB', '\u041f\u0411', '\u0414\u0430\u043d\u043d\u044b\u0435'),\n            \n            # === \u0421\u041a\u041e\u0420\u041e\u0421\u0422\u042c \u041f\u0415\u0420\u0415\u0414\u0410\u0427\u0418 \u0414\u0410\u041d\u041d\u042b\u0425 ===\n            r'\\b\\d+\\.?\\d*\\s*bps\\b': ('bps', '\u0431\u0438\u0442/\u0441', '\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*kbps\\b': ('kbps', '\u043a\u0431\u0438\u0442/\u0441', '\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*Mbps\\b': ('Mbps', '\u041c\u0431\u0438\u0442/\u0441', '\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*Gbps\\b': ('Gbps', '\u0413\u0431\u0438\u0442/\u0441', '\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*Tbps\\b': ('Tbps', '\u0422\u0431\u0438\u0442/\u0441', '\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c'),\n            \n            # === \u0418\u0417\u041e\u0411\u0420\u0410\u0416\u0415\u041d\u0418\u042f \u0418 \u0412\u0418\u0414\u0415\u041e ===\n            r'\\b\\d+\\.?\\d*\\s*mp\\b': ('mp', '\u043c\u043f', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*MP\\b': ('MP', '\u041c\u041f', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*px\\b': ('px', '\u043f\u0438\u043a\u0441', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*dpi\\b': ('dpi', '\u0442\u043e\u0447\u0435\u043a/\u0434\u044e\u0439\u043c', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*ppi\\b': ('ppi', '\u043f\u0438\u043a\u0441/\u0434\u044e\u0439\u043c', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*lpi\\b': ('lpi', '\u043b\u0438\u043d\u0438\u0439/\u0434\u044e\u0439\u043c', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            \n            # === \u0417\u0412\u0423\u041a \u0418 \u041e\u0421\u0412\u0415\u0429\u0415\u041d\u041d\u041e\u0421\u0422\u042c ===\n            r'\\b\\d+\\.?\\d*\\s*dB\\b': ('dB', '\u0434\u0411', '\u0417\u0432\u0443\u043a'),\n            r'\\b\\d+\\.?\\d*\\s*dBA\\b': ('dBA', '\u0434\u0411\u0410', '\u0417\u0432\u0443\u043a'),\n            r'\\b\\d+\\.?\\d*\\s*dBC\\b': ('dBC', '\u0434\u0411\u0421', '\u0417\u0432\u0443\u043a'),\n            r'\\b\\d+\\.?\\d*\\s*dBi\\b': ('dBi', '\u0434\u0411\u0438', '\u0417\u0432\u0443\u043a'),\n            r'\\b\\d+\\.?\\d*\\s*dBm\\b': ('dBm', '\u0434\u0411\u043c', '\u0417\u0432\u0443\u043a'),\n            r'\\b\\d+\\.?\\d*\\s*dBW\\b': ('dBW', '\u0434\u0411\u0412\u0442', '\u0417\u0432\u0443\u043a'),\n            r'\\b\\d+\\.?\\d*\\s*lux\\b': ('lux', '\u043b\u043a', '\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*lm\\b': ('lm', '\u043b\u043c', '\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*cd\\b': ('cd', '\u043a\u0434', '\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*nit\\b': ('nit', '\u043d\u0438\u0442', '\u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c'),\n            \n            # === \u0422\u0415\u041c\u041f\u0415\u0420\u0410\u0422\u0423\u0420\u0410 (\u0442\u043e\u043b\u044c\u043a\u043e \u0426\u0435\u043b\u044c\u0441\u0438\u0439!) ===\n            r'\\b\\d+\\.?\\d*\\s*\u00b0C\\b': ('\u00b0C', '\u00b0C', '\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*C\\b': ('C', '\u00b0C', '\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*K\\b': ('K', '\u041a', '\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430'),\n            \n            # === \u0414\u0410\u0412\u041b\u0415\u041d\u0418\u0415 \u0418 \u0412\u041b\u0410\u0416\u041d\u041e\u0421\u0422\u042c ===\n            r'\\b\\d+\\.?\\d*\\s*Pa\\b': ('Pa', '\u041f\u0430', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*kPa\\b': ('kPa', '\u043a\u041f\u0430', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*MPa\\b': ('MPa', '\u041c\u041f\u0430', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*bar\\b': ('bar', '\u0431\u0430\u0440', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*mbar\\b': ('mbar', '\u043c\u0431\u0430\u0440', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*psi\\b': ('psi', '\u0444\u0443\u043d\u0442/\u043a\u0432.\u0434\u044e\u0439\u043c', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*atm\\b': ('atm', '\u0430\u0442\u043c', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*mmHg\\b': ('mmHg', '\u043c\u043c \u0440\u0442.\u0441\u0442.', '\u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+\\.?\\d*\\s*%RH\\b': ('%RH', '%\u041e\u0412', '\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c'),\n            r'\\b\\d+\\.?\\d*\\s*RH\\b': ('RH', '%\u041e\u0412', '\u0412\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c'),\n            \n            # === \u0423\u0413\u041b\u042b ===\n            r'\\b\\d+\\.?\\d*\\s*\u00b0\\b': ('\u00b0', '\u00b0', '\u0423\u0433\u043e\u043b'),\n            r'\\b\\d+\\.?\\d*\\s*degrees?\\b': ('degree', '\u00b0', '\u0423\u0433\u043e\u043b'),\n            \n            # === \u0421\u041f\u0415\u0426\u0418\u0410\u041b\u042c\u041d\u042b\u0415 \u0415\u0414\u0418\u041d\u0418\u0426\u042b \u0412\u0418\u0414\u0415\u041e\u041d\u0410\u0411\u041b\u042e\u0414\u0415\u041d\u0418\u042f ===\n            r'\\b\\d+\\.?\\d*\\s*FOV\\b': ('FOV', '\u043f\u043e\u043b\u0435 \u0437\u0440\u0435\u043d\u0438\u044f', '\u041e\u043f\u0442\u0438\u043a\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*AOV\\b': ('AOV', '\u0443\u0433\u043e\u043b \u043e\u0431\u0437\u043e\u0440\u0430', '\u041e\u043f\u0442\u0438\u043a\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*HFOV\\b': ('HFOV', '\u0433\u043e\u0440\u0438\u0437. \u043e\u0431\u0437\u043e\u0440', '\u041e\u043f\u0442\u0438\u043a\u0430'),\n            r'\\b\\d+\\.?\\d*\\s*VFOV\\b': ('VFOV', '\u0432\u0435\u0440\u0442. \u043e\u0431\u0437\u043e\u0440', '\u041e\u043f\u0442\u0438\u043a\u0430'),\n            \n            # === \u0420\u0410\u0417\u0420\u0415\u0428\u0415\u041d\u0418\u042f \u0412\u0418\u0414\u0415\u041e ===\n            r'\\b1920x1080\\b': ('1920x1080', 'FullHD', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b3840x2160\\b': ('3840x2160', '4K', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b1280x720\\b': ('1280x720', 'HD', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n            r'\\b\\d+x\\d+\\b': ('resolution', '\u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435', '\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0435'),\n        }\n        \n        found_units = {}\n        examples = {}\n        \n        # \u0418\u0449\u0435\u043c \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445\n        for value in all_values:\n            for pattern, (unit, translation, category) in unit_patterns.items():\n                if re.search(pattern, value, re.IGNORECASE):\n                    if unit not in found_units:\n                        found_units[unit] = (translation, category)\n                        examples[unit] = []\n                    if value not in examples[unit] and len(examples[unit]) < 3:\n                        examples[unit].append(value)\n        \n        if not found_units:\n            messagebox.showinfo(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\", \"\u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b\")\n            return\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043a\u0430\u043a\u0438\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0445\n        existing_units = {rule['original'] for rule in self.value_translation_rules}\n        new_units = {}\n        \n        for unit, (translation, category) in found_units.items():\n            if unit not in existing_units:\n                new_units[unit] = (translation, category, examples[unit])\n        \n        if not new_units:\n            messagebox.showinfo(\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\", \n                              f\"\u041d\u0430\u0439\u0434\u0435\u043d\u043e \u0435\u0434\u0438\u043d\u0438\u0446: {len(found_units)}\\n\u0412\u0441\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0445\")\n            return\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0441 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n        message = f\"\ud83d\udd0d \u041d\u0430\u0439\u0434\u0435\u043d\u043e \u043d\u043e\u0432\u044b\u0445 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f: {len(new_units)}\\n\\n\"\n        for unit, (translation, category, unit_examples) in new_units.items():\n            message += f\"\u2022 {unit} \u2192 {translation} ({category})\\n\"\n            if unit_examples:\n                message += f\"  \u041f\u0440\u0438\u043c\u0435\u0440\u044b: {', '.join(unit_examples[:2])}\\n\"\n        \n        message += f\"\\n\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430?\"\n        \n        if messagebox.askyesno(\"\u041d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\", message):\n            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n            for unit, (translation, category, unit_examples) in new_units.items():\n                new_rule = {\n                    \"active\": True,\n                    \"original\": unit,\n                    \"translation\": translation,\n                    \"category\": category,\n                    \"description\": f\"\u0415\u0434\u0438\u043d\u0438\u0446\u0430 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f: {translation}\",\n                    \"examples\": f\"{unit_examples[0] if unit_examples else f'100{unit}'} \u2192 {unit_examples[0].replace(unit, translation) if unit_examples else f'100 {translation}'}\"\n                }\n                self.value_translation_rules.append(new_rule)\n            \n            self.save_value_translation_rules()\n            self.refresh_translation_rules_display()\n            \n            messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e {len(new_units)} \u043d\u043e\u0432\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\")\n\n    def activate_all_translation_rules(self):\n        \"\"\"\u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442 \u0432\u0441\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        for rule in self.value_translation_rules:\n            rule[\"active\"] = True\n        self.save_value_translation_rules()\n        self.refresh_translation_rules_display()\n\n    def deactivate_all_translation_rules(self):\n        \"\"\"\u0414\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442 \u0432\u0441\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        for rule in self.value_translation_rules:\n            rule[\"active\"] = False\n        self.save_value_translation_rules()\n        self.refresh_translation_rules_display()\n\n    def on_translation_tree_click(self, event):\n        \"\"\"\ud83c\udd95 \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043b\u0438\u043a\u0430 \u043f\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 v4.8\"\"\"\n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0447\u0435\u0442\u0447\u0438\u043a \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432\n        if hasattr(self, 'selected_count_label'):\n            selected_count = len(self.translation_tree.selection())\n            self.selected_count_label.config(text=f\"\u0412\u044b\u0431\u0440\u0430\u043d\u043e: {selected_count}\")\n\n    def activate_selected_rules(self):\n        \"\"\"\ud83c\udd95 \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 v4.8\"\"\"\n        selection = self.translation_tree.selection()\n        if not selection:\n            messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0434\u043b\u044f \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438\")\n            return\n        \n        activated_count = 0\n        for item in selection:\n            index = self.translation_tree.index(item)\n            if index < len(self.value_translation_rules):\n                if not self.value_translation_rules[index].get(\"active\", True):\n                    self.value_translation_rules[index][\"active\"] = True\n                    activated_count += 1\n        \n        if activated_count > 0:\n            self.save_value_translation_rules()\n            self.refresh_translation_rules_display()\n            print(f\"\u2705 \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043e {activated_count} \u043f\u0440\u0430\u0432\u0438\u043b\")\n        else:\n            print(\"\u2139\ufe0f \u0412\u0441\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0443\u0436\u0435 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\")\n\n    def deactivate_selected_rules(self):\n        \"\"\"\ud83c\udd95 \u0414\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 v4.8\"\"\"\n        selection = self.translation_tree.selection()\n        if not selection:\n            messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0434\u043b\u044f \u0434\u0435\u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438\")\n            return\n        \n        deactivated_count = 0\n        for item in selection:\n            index = self.translation_tree.index(item)\n            if index < len(self.value_translation_rules):\n                if self.value_translation_rules[index].get(\"active\", True):\n                    self.value_translation_rules[index][\"active\"] = False\n                    deactivated_count += 1\n        \n        if deactivated_count > 0:\n            self.save_value_translation_rules()\n            self.refresh_translation_rules_display()\n            print(f\"\u2705 \u0414\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043e {deactivated_count} \u043f\u0440\u0430\u0432\u0438\u043b\")\n        else:\n            print(\"\u2139\ufe0f \u0412\u0441\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0443\u0436\u0435 \u043d\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\")\n\n    def export_value_translations(self):\n        \"\"\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0432 \u0444\u0430\u0439\u043b\"\"\"\n        filename = filedialog.asksaveasfilename(\n            title=\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\",\n            defaultextension=\".json\",\n            filetypes=[(\"JSON \u0444\u0430\u0439\u043b\u044b\", \"*.json\"), (\"\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b\", \"*.*\")]\n        )\n        \n        if filename:\n            try:\n                with open(filename, \"w\", encoding=\"utf-8\") as f:\n                    json.dump(self.value_translation_rules, f, ensure_ascii=False, indent=2)\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432:\\n{filename}\")\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430:\\n{e}\")\n\n    def import_value_translations(self):\n        \"\"\"\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430\"\"\"\n        filename = filedialog.askopenfilename(\n            title=\"\u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\",\n            filetypes=[(\"JSON \u0444\u0430\u0439\u043b\u044b\", \"*.json\"), (\"\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b\", \"*.*\")]\n        )\n        \n        if filename:\n            try:\n                with open(filename, \"r\", encoding=\"utf-8\") as f:\n                    imported_rules = json.load(f)\n                \n                # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u0430\u043d\u043d\u044b\u0445\n                if not isinstance(imported_rules, list):\n                    messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442 \u0444\u0430\u0439\u043b\u0430\")\n                    return\n                \n                # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n                existing_originals = {rule['original'] for rule in self.value_translation_rules}\n                added_count = 0\n                \n                for rule in imported_rules:\n                    if isinstance(rule, dict) and 'original' in rule and 'translation' in rule:\n                        if rule['original'] not in existing_originals:\n                            # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u043e\u043b\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e\n                            complete_rule = {\n                                \"active\": rule.get(\"active\", True),\n                                \"original\": rule[\"original\"],\n                                \"translation\": rule[\"translation\"],\n                                \"category\": rule.get(\"category\", \"\u0418\u043c\u043f\u043e\u0440\u0442\"),\n                                \"description\": rule.get(\"description\", \"\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e\"),\n                                \"examples\": rule.get(\"examples\", f\"{rule['original']} \u2192 {rule['translation']}\")\n                            }\n                            self.value_translation_rules.append(complete_rule)\n                            existing_originals.add(rule['original'])\n                            added_count += 1\n                \n                self.save_value_translation_rules()\n                self.refresh_translation_rules_display()\n                \n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e {added_count} \u043d\u043e\u0432\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b\")\n                \n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0430:\\n{e}\")\n\n    def show_translation_context_menu(self, event):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u0434\u043b\u044f \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u043e\u0434 \u043a\u0443\u0440\u0441\u043e\u0440\u043e\u043c\n        item = self.translation_tree.identify('item', event.x, event.y)\n        if item:\n            self.translation_tree.selection_set(item)\n            \n            # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e\n            context_menu = Menu(self.root, tearoff=0)\n            context_menu.add_command(label=\"\u270f\ufe0f \u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", command=self.edit_value_translation_rule)\n            context_menu.add_command(label=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=self.delete_value_translation_rule)\n            context_menu.add_separator()\n            \n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n            index = self.translation_tree.index(item)\n            if 0 <= index < len(self.value_translation_rules):\n                rule = self.value_translation_rules[index]\n                is_active = rule.get(\"active\", True)\n                \n                if is_active:\n                    context_menu.add_command(label=\"\u2717 \u0414\u0435\u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", \n                                           command=lambda: self.toggle_translation_rule(index))\n                else:\n                    context_menu.add_command(label=\"\u2713 \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c\", \n                                           command=lambda: self.toggle_translation_rule(index))\n            \n            context_menu.tk_popup(event.x_root, event.y_root)\n\n    def toggle_translation_rule(self, index):\n        \"\"\"\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        if 0 <= index < len(self.value_translation_rules):\n            rule = self.value_translation_rules[index]\n            rule[\"active\"] = not rule.get(\"active\", True)\n            self.save_value_translation_rules()\n            self.refresh_translation_rules_display()\n\n    def load_merge_settings(self):\n        \"\"\"\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"\n        # \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        if os.path.exists(self.PARAM_MERGE_SETTINGS_FILE):\n            try:\n                with open(self.PARAM_MERGE_SETTINGS_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.param_merge_settings = json.load(f)\n            except Exception as e:\n                print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {e}\")\n                self.param_merge_settings = {}\n        else:\n            self.param_merge_settings = {}\n\n        # \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        if os.path.exists(self.VALUE_MERGE_SETTINGS_FILE):\n            try:\n                with open(self.VALUE_MERGE_SETTINGS_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.value_merge_settings = json.load(f)\n            except Exception as e:\n                print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {e}\")\n                self.value_merge_settings = {}\n        else:\n            self.value_merge_settings = {}\n\n    def save_merge_settings(self):\n        \"\"\"\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"\n        try:\n            with open(self.PARAM_MERGE_SETTINGS_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.param_merge_settings, f, ensure_ascii=False, indent=2)\n        except Exception as e:\n            print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {e}\")\n\n        try:\n            with open(self.VALUE_MERGE_SETTINGS_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.value_merge_settings, f, ensure_ascii=False, indent=2)\n        except Exception as e:\n            print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {e}\")\n\n    def show_help_dialog(self):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u0441\u043f\u0440\u0430\u0432\u043a\u0438 \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0435\u0439\"\"\"\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\u043e\u0432\u043e\u0435 \u043e\u043a\u043d\u043e\n        help_dialog = Toplevel(self.root)\n        help_dialog.title(\"\ud83d\udcda \u0421\u043f\u0440\u0430\u0432\u043a\u0430 - \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\")\n        help_dialog.geometry(\"800x600\")\n        help_dialog.transient(self.root)\n        help_dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        help_dialog.geometry(\"+%d+%d\" % (self.root.winfo_rootx() + 100, self.root.winfo_rooty() + 50))\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0444\u0440\u0435\u0439\u043c\n        main_frame = Frame(help_dialog)\n        main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)\n        \n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\n        header_frame = Frame(main_frame)\n        header_frame.pack(fill=X, pady=(0, 10))\n        \n        title_label = Label(header_frame, text=\"\ud83d\udcda \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\", \n                           font=(\"Arial\", 16, \"bold\"), fg=\"#2E7D32\")\n        title_label.pack()\n        \n        version_label = Label(header_frame, text=\"\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 v4.2\", \n                             font=(\"Arial\", 10), fg=\"#666666\")\n        version_label.pack()\n        \n        # \u041d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f (\u043a\u043d\u043e\u043f\u043a\u0438 \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0430)\n        nav_frame = LabelFrame(main_frame, text=\"\ud83e\udded \u0411\u044b\u0441\u0442\u0440\u0430\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044f\", font=(\"Arial\", 9, \"bold\"))\n        nav_frame.pack(fill=X, pady=(0, 10))\n        \n        nav_buttons = Frame(nav_frame)\n        nav_buttons.pack(fill=X, padx=5, pady=5)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043d\u043e\u043f\u043a\u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438\n        nav_sections = [\n            (\"\ud83d\ude80 \u0411\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u0442\u0430\u0440\u0442\", \"start\"),\n            (\"\ud83d\udd27 \u0418\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\", \"instruction\"), \n            (\"\u2699\ufe0f \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\", \"settings\"),\n            (\"\ud83d\udca1 \u041f\u0440\u0438\u043c\u0435\u0440\u044b\", \"examples\"),\n            (\"\ud83d\udd27 \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b\", \"problems\"),\n            (\"\u2328\ufe0f \u041a\u043b\u0430\u0432\u0438\u0448\u0438\", \"hotkeys\")\n        ]\n        \n        for i, (text, section) in enumerate(nav_sections):\n            btn = Button(nav_buttons, text=text, width=15, \n                        command=lambda s=section, ht=None: self.scroll_to_section_in_dialog(ht, s))\n            btn.grid(row=i//3, column=i%3, padx=2, pady=2, sticky=W+E)\n        \n        # \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043a\u043e\u043b\u043e\u043d\u043a\u0438 \u0434\u043b\u044f \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n        for i in range(3):\n            nav_buttons.grid_columnconfigure(i, weight=1)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u0441 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u043e\u0439\n        text_frame = Frame(main_frame)\n        text_frame.pack(fill=BOTH, expand=True)\n        \n        # \u0422\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435\n        help_text = Text(text_frame, wrap=WORD, font=(\"Arial\", 10), \n                        padx=10, pady=10, bg=\"#FAFAFA\")\n        \n        # \u0421\u043a\u0440\u043e\u043b\u043b\u0431\u0430\u0440\n        scrollbar = Scrollbar(text_frame, orient=VERTICAL, command=help_text.yview)\n        help_text.configure(yscrollcommand=scrollbar.set)\n        \n        # \u0420\u0430\u0437\u043c\u0435\u0449\u0435\u043d\u0438\u0435\n        help_text.pack(side=LEFT, fill=BOTH, expand=True)\n        scrollbar.pack(side=RIGHT, fill=Y)\n        \n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0441\u0442\u0438\u043b\u0435\u0439 \u0442\u0435\u043a\u0441\u0442\u0430\n        help_text.tag_configure(\"title\", font=(\"Arial\", 14, \"bold\"), foreground=\"#1976D2\", spacing1=10, spacing3=5)\n        help_text.tag_configure(\"subtitle\", font=(\"Arial\", 12, \"bold\"), foreground=\"#388E3C\", spacing1=8, spacing3=3)\n        help_text.tag_configure(\"header\", font=(\"Arial\", 11, \"bold\"), foreground=\"#F57C00\", spacing1=6, spacing3=2)\n        help_text.tag_configure(\"code\", font=(\"Courier New\", 9), background=\"#F5F5F5\", foreground=\"#D32F2F\")\n        help_text.tag_configure(\"emphasis\", font=(\"Arial\", 10, \"bold\"), foreground=\"#7B1FA2\")\n        help_text.tag_configure(\"success\", foreground=\"#2E7D32\")\n        help_text.tag_configure(\"warning\", foreground=\"#F57C00\")\n        help_text.tag_configure(\"error\", foreground=\"#D32F2F\")\n        \n        # \u0421\u043b\u043e\u0432\u0430\u0440\u044c \u043f\u043e\u0437\u0438\u0446\u0438\u0439 \u0441\u0435\u043a\u0446\u0438\u0439 \u0434\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438\n        section_positions = {}\n        \n        # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438\n        self.load_help_content_in_dialog(help_text, section_positions)\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043a\u043d\u043e\u043f\u043e\u043a \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c\u0438 \u0441\u0441\u044b\u043b\u043a\u0430\u043c\u0438\n        for i, (text, section) in enumerate(nav_sections):\n            btn = nav_buttons.grid_slaves(row=i//3, column=i%3)[0]\n            btn.config(command=lambda s=section: self.scroll_to_section_in_dialog(help_text, section_positions, s))\n        \n        # \u0414\u0435\u043b\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f\n        help_text.config(state=DISABLED)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0430 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f\n        close_frame = Frame(main_frame)\n        close_frame.pack(fill=X, pady=(10, 0))\n        \n        close_btn = Button(close_frame, text=\"\u0417\u0430\u043a\u0440\u044b\u0442\u044c\", command=help_dialog.destroy, width=15)\n        close_btn.pack(side=RIGHT)\n        \n        # \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043b\u0430\u0432\u0438\u0448\n        help_dialog.bind('<Escape>', lambda e: help_dialog.destroy())\n        help_dialog.focus_set()\n\n    def scroll_to_section_in_dialog(self, help_text, section_positions, section):\n        \"\"\"\u041f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u043a \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u0441\u0435\u043a\u0446\u0438\u0438 \u0432 \u0434\u0438\u0430\u043b\u043e\u0433\u0435\"\"\"\n        if section in section_positions:\n            help_text.see(section_positions[section])\n            # \u041f\u043e\u0434\u0441\u0432\u0435\u0447\u0438\u0432\u0430\u0435\u043c \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u0443\u044e \u0441\u0435\u043a\u0446\u0438\u044e\n            help_text.tag_add(SEL, section_positions[section], \n                             f\"{section_positions[section]} lineend\")\n\n    def load_help_content_in_dialog(self, help_text, section_positions):\n        \"\"\"\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0432 \u0434\u0438\u0430\u043b\u043e\u0433\"\"\"\n        help_text.config(state=NORMAL)\n        \n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435\n        help_text.delete(1.0, END)\n        \n        content = '''\ud83c\udfaf \u041d\u0410\u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0415 \u041f\u0420\u041e\u0413\u0420\u0410\u041c\u041c\u042b\n\n\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 Excel \u0444\u0430\u0439\u043b\u043e\u0432 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0445 \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442.\n\n\u0427\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430:\n\ud83d\udce5 \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 Excel \u0444\u0430\u0439\u043b\u044b \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438\n\ud83d\udd27 \u041f\u0430\u0440\u0441\u0438\u0442 HTML \u0438 \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0442\u043e\u0432\u0430\u0440\u043e\u0432  \n\u2702\ufe0f \u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \"\u041a\u0440\u0430\u0441\u043d\u044b\u0439, \u0421\u0438\u043d\u0438\u0439\" \u2192 \"\u041a\u0440\u0430\u0441\u043d\u044b\u0439; \u0421\u0438\u043d\u0438\u0439\")\n\ud83d\udd04 \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (\u043c\u0430\u043f\u043f\u0438\u043d\u0433)\n\ud83d\udcbe \u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 \u043d\u043e\u0432\u044b\u0439 Excel \u0444\u0430\u0439\u043b\n\n---\n\n\ud83d\ude80 \u0411\u042b\u0421\u0422\u0420\u042b\u0419 \u0421\u0422\u0410\u0420\u0422\n\n1. \u041f\u041e\u0414\u0413\u041e\u0422\u041e\u0412\u041a\u0410 \u0424\u0410\u0419\u041b\u0410\n\u0412\u0430\u0448 Excel \u0444\u0430\u0439\u043b \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c:\n\u2022 \u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0441 \u0430\u0440\u0442\u0438\u043a\u0443\u043b\u0430\u043c\u0438 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 (\u043e\u0431\u044b\u0447\u043d\u043e \u043f\u0435\u0440\u0432\u044b\u0439)\n\u2022 \u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 (\u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0435 \u043d\u043e\u043c\u0435\u0440)  \n\u2022 \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e)\n\n2. \u041f\u0420\u0418\u041c\u0415\u0420 \u0418\u0421\u0425\u041e\u0414\u041d\u042b\u0425 \u0414\u0410\u041d\u041d\u042b\u0425\n\u0410\u0440\u0442\u0438\u043a\u0443\u043b | \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435  | \u0425\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438 (HTML)\nTOV001  | \u0422\u043e\u0432\u0430\u0440 1   | <tr><td>\u0426\u0432\u0435\u0442</td><td>\u041a\u0440\u0430\u0441\u043d\u044b\u0439</td></tr>\nTOV002  | \u0422\u043e\u0432\u0430\u0440 2   | <tr><td>\u0420\u0430\u0437\u043c\u0435\u0440</td><td>XL</td></tr>\n\n3. \u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422 \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0418  \n\u0410\u0440\u0442\u0438\u043a\u0443\u043b | \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435  | \u0426\u0432\u0435\u0442     | \u0420\u0430\u0437\u043c\u0435\u0440\nTOV001  | \u0422\u043e\u0432\u0430\u0440 1   | \u041a\u0440\u0430\u0441\u043d\u044b\u0439  |\nTOV002  | \u0422\u043e\u0432\u0430\u0440 2   |          | XL\n\n---\n\n\ud83d\udd27 \u041f\u041e\u0428\u0410\u0413\u041e\u0412\u0410\u042f \u0418\u041d\u0421\u0422\u0420\u0423\u041a\u0426\u0418\u042f\n\n\u0428\u0410\u0413 1: \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u0444\u0430\u0439\u043b\u043e\u0432 \u0438 \u043f\u0443\u0442\u0435\u0439\n\n\ud83d\udcc1 \u0424\u0410\u0419\u041b\u042b \u0418 \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418:\n1. \u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0444\u0430\u0439\u043b: \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \ud83d\udcc2 \u0440\u044f\u0434\u043e\u043c \u0441 \u043f\u043e\u043b\u0435\u043c \"\u0424\u0430\u0439\u043b\"\n2. \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f: \u043d\u0430\u0436\u043c\u0438\u0442\u0435 \ud83d\udcc2 \u0440\u044f\u0434\u043e\u043c \u0441 \u043f\u043e\u043b\u0435\u043c \"\u041f\u0430\u043f\u043a\u0430\"\n3. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b:\n   \u2022 \u0421\u0442\u043e\u043b\u0431\u0435\u0446: \u041d\u043e\u043c\u0435\u0440 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0441 HTML (\u043e\u0442\u0441\u0447\u0435\u0442 \u0441 1)\n   \u2022 \u0414\u043e\u043f.\u0441\u0442\u043e\u043b\u0431\u0446\u044b: \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u2699\ufe0f \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\n\n\u0428\u0410\u0413 2: \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430\n\n\ud83d\udd27 \u0414\u0415\u0419\u0421\u0422\u0412\u0418\u042f:\n1. \ud83d\udce5 \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c - \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0444\u0430\u0439\u043b \u0432 \u043f\u0430\u043c\u044f\u0442\u044c\n2. \u2699\ufe0f \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c - \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438\u0437 HTML  \n3. \ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c - \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 Excel\n\n\u041f\u0420\u041e\u0426\u0415\u0421\u0421 \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0418:\n1. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \ud83d\udce5 \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u0430\n2. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u2699\ufe0f \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0434\u043b\u044f \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n3. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \"\ud83d\udccb \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u0430\"\n4. \u041f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n5. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0434\u043b\u044f \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430\n\n---\n\n\u2699\ufe0f \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418 \u0418 \u041a\u041e\u041d\u0424\u0418\u0413\u0423\u0420\u0410\u0426\u0418\u042f\n\n\u0411\u042b\u0421\u0422\u0420\u042b\u0415 \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418 (\u043f\u0430\u043d\u0435\u043b\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432):\n\n\ud83d\udd04 \u041f\u0420\u0410\u0412\u0418\u041b\u0410 \u0417\u0410\u041c\u0415\u041d\n\u2022 \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 regex-\u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u0435\u043a\u0441\u0442\u0430\n\u2022 \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043b\u0438\u0448\u043d\u0438\u0445 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432\n\u2022 \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n\u2022 \u041f\u0440\u0438\u043c\u0435\u0440: \u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0432\u0435\u0440\u0441\u0438\u0439 \\\\sv?\\\\d+(\\\\.\\\\d+)*$ \u2192 (\u043f\u0443\u0441\u0442\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430)\n\nAa \u041d\u041e\u0420\u041c\u0410\u041b\u0418\u0417\u0410\u0426\u0418\u042f \u0420\u0415\u0413\u0418\u0421\u0422\u0420\u0410  \n\u2022 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a \u0435\u0434\u0438\u043d\u043e\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0443\n\u2022 \"support\", \"Support\", \"SUPPORT\" \u2192 \"Support\"\n\u2022 \u041f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u0437-\u0437\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n\n\u2248 \u0421\u0418\u041d\u041e\u041d\u0418\u041c\u042b \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419\n\u2022 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0437\u0430\u043c\u0435\u043d\u0430 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n\u2022 \"Yes\" \u2192 \"Supported\", \"No\" \u2192 \"Not Supported\"  \n\u2022 \u0423\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n\n\ud83d\udee1\ufe0f \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0415 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u042f\n\u2022 \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u041d\u0415 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f\n\u2022 \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \"B/W\", \"I/O\", \"AC/DC\"\n\u2022 \u041e\u0441\u0442\u0430\u044e\u0442\u0441\u044f \u043a\u0430\u043a \u0435\u0441\u0442\u044c, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438\n\n---\n\n\ud83d\udca1 \u041f\u0420\u0418\u041c\u0415\u0420\u042b \u0418\u0421\u041f\u041e\u041b\u042c\u0417\u041e\u0412\u0410\u041d\u0418\u042f\n\n\u041f\u0420\u0418\u041c\u0415\u0420 1: \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u0430 \u0442\u043e\u0432\u0430\u0440\u043e\u0432\n\n\u0418\u0421\u0425\u041e\u0414\u041d\u042b\u0415 \u0414\u0410\u041d\u041d\u042b\u0415:\n<table>\n<tr><td>\u0411\u0440\u0435\u043d\u0434</td><td>Samsung</td></tr>\n<tr><td>\u041c\u043e\u0434\u0435\u043b\u044c</td><td>Galaxy S21</td></tr>  \n<tr><td>\u0426\u0432\u0435\u0442</td><td>\u0427\u0435\u0440\u043d\u044b\u0439, \u0411\u0435\u043b\u044b\u0439</td></tr>\n<tr><td>\u041f\u0430\u043c\u044f\u0442\u044c</td><td>128GB, 256GB</td></tr>\n</table>\n\n\u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422:\n\u0410\u0440\u0442\u0438\u043a\u0443\u043b | \u0411\u0440\u0435\u043d\u0434   | \u041c\u043e\u0434\u0435\u043b\u044c     | \u0426\u0432\u0435\u0442           | \u041f\u0430\u043c\u044f\u0442\u044c\nPHONE01 | Samsung | Galaxy S21 | \u0427\u0435\u0440\u043d\u044b\u0439; \u0411\u0435\u043b\u044b\u0439  | 128GB; 256GB\n\n\u041f\u0420\u0418\u041c\u0415\u0420 2: \u041c\u0430\u043f\u043f\u0438\u043d\u0433 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n\n\u041f\u0420\u041e\u0411\u041b\u0415\u041c\u0410: \u0420\u0430\u0437\u043d\u044b\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n\"\u0426\u0432\u0435\u0442 \u043a\u043e\u0440\u043f\u0443\u0441\u0430\" \u2192 \"\u041a\u0440\u0430\u0441\u043d\u044b\u0439\"\n\"Color\" \u2192 \"Blue\"\n\"\u0420\u0430\u0441\u0446\u0432\u0435\u0442\u043a\u0430\" \u2192 \"\u0417\u0435\u043b\u0435\u043d\u044b\u0439\"\n\n\u0420\u0415\u0428\u0415\u041d\u0418\u0415: \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0438\n1. \u0426\u0432\u0435\u0442 \u043a\u043e\u0440\u043f\u0443\u0441\u0430 \u2192 \u0426\u0432\u0435\u0442\n2. Color \u2192 \u0426\u0432\u0435\u0442  \n3. \u0420\u0430\u0441\u0446\u0432\u0435\u0442\u043a\u0430 \u2192 \u0426\u0432\u0435\u0442\n\n\u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422: \u0412\u0441\u0435 \u0432 \u043e\u0434\u043d\u043e\u043c \u0441\u0442\u043e\u043b\u0431\u0446\u0435 \"\u0426\u0432\u0435\u0442\"\n\n---\n\n\ud83d\udd27 \u0420\u0415\u0428\u0415\u041d\u0418\u0415 \u041f\u0420\u041e\u0411\u041b\u0415\u041c\n\n\u0427\u0410\u0421\u0422\u042b\u0415 \u041e\u0428\u0418\u0411\u041a\u0418 \u0418 \u0420\u0415\u0428\u0415\u041d\u0418\u042f:\n\n\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\"\n\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442\n\u2705 \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0444\u0430\u0439\u043b \u043d\u0435 \u043e\u0442\u043a\u0440\u044b\u0442 \u0432 Excel\n\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0444\u043e\u0440\u043c\u0430\u0442 \u0444\u0430\u0439\u043b\u0430 (.xlsx, .xls)\n\n\"\u0421\u0442\u043e\u043b\u0431\u0435\u0446 \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u0442\u0430\u0431\u043b\u0438\u0446\u044b\"\n\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0441 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438  \n\u2705 \u041e\u0442\u0441\u0447\u0435\u0442 \u0432\u0435\u0434\u0435\u0442\u0441\u044f \u0441 1, \u0430 \u043d\u0435 \u0441 0\n\u2705 \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442\n\n\"\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0438\"\n\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0444\u043e\u0440\u043c\u0430\u0442 HTML \u0432 \u0441\u0442\u043e\u043b\u0431\u0446\u0435\n\u2705 \u0414\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0442\u0435\u0433\u0438 <tr> \u0438 <td>\n\u2705 \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u043d\u0435 \u043f\u0443\u0441\u0442\u043e\u0439\n\n\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\"\n\u2705 \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u044c \u0432 \u043f\u0430\u043f\u043a\u0443\n\u2705 \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u043f\u0430\u043f\u043a\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442\n\u2705 \u0417\u0430\u043a\u0440\u043e\u0439\u0442\u0435 Excel, \u0435\u0441\u043b\u0438 \u043e\u0442\u043a\u0440\u044b\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u0444\u0430\u0439\u043b\n\n---\n\n\u2328\ufe0f \u0413\u041e\u0420\u042f\u0427\u0418\u0415 \u041a\u041b\u0410\u0412\u0418\u0428\u0418\n\n\u0424\u0410\u0419\u041b\u041e\u0412\u042b\u0415 \u041e\u041f\u0415\u0420\u0410\u0426\u0418\u0418:\nCtrl+O - \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\nCtrl+P - \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\nCtrl+S - \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442  \nCtrl+Q - \u0412\u044b\u0445\u043e\u0434 \u0438\u0437 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b\n\n\u0420\u0410\u0411\u041e\u0422\u0410 \u0421 \u0414\u0410\u041d\u041d\u042b\u041c\u0418:\nCtrl+C - \u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435\nCtrl+V - \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\nCtrl+Z - \u041e\u0442\u043c\u0435\u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f\nCtrl+A - \u0412\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0432\u0441\u0435\nDelete - \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435\n\n\u041d\u0410\u0412\u0418\u0413\u0410\u0426\u0418\u042f:\nF1 - \u0421\u043f\u0440\u0430\u0432\u043a\u0430\nEsc - \u0417\u0430\u043a\u0440\u044b\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433\nEnter - \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\nTab - \u041f\u0435\u0440\u0435\u0445\u043e\u0434 \u043c\u0435\u0436\u0434\u0443 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c\u0438\n\n---\n\n\ud83c\udfaf \u041b\u0423\u0427\u0428\u0418\u0415 \u041f\u0420\u0410\u041a\u0422\u0418\u041a\u0418\n\n\u041f\u041e\u0414\u0413\u041e\u0422\u041e\u0412\u041a\u0410 \u0414\u0410\u041d\u041d\u042b\u0425:\n1. \u041e\u0447\u0438\u0441\u0442\u0438\u0442\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043e\u0442 \u044f\u0432\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a\n2. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0438\u0440\u0443\u0439\u0442\u0435 HTML \u043f\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438\n3. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430\n4. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0443 \u0444\u0430\u0439\u043b\u0430 (UTF-8 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u043e)\n\n\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0410 \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0418:\n1. \u041d\u0430\u0447\u043d\u0438\u0442\u0435 \u0441 \u043c\u0430\u043b\u043e\u0433\u043e - \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 10-20 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f  \n2. \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u0442\u0435 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0438 \u0434\u043b\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n3. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n4. \u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0434\u043b\u044f \u0443\u043d\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\n\u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422\u041e\u0412:\n1. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0432\u0430\u0440\u043e\u0432 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435\n2. \u0421\u0440\u0430\u0432\u043d\u0438\u0442\u0435 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438\n3. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c \u0432 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n4. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u0442\u0435\u0440\u0438 \u0432\u0430\u0436\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445\n\n---\n\n\ud83c\udfc6 \u0417\u0410\u041a\u041b\u042e\u0427\u0415\u041d\u0418\u0415\n\n\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 v4.2 - \u043c\u043e\u0449\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u043d\u044b\u0445 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a.\n\n\u041f\u0420\u0415\u0418\u041c\u0423\u0429\u0415\u0421\u0422\u0412\u0410:\n\u26a1 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0443\u0442\u0438\u043d\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439\n\ud83c\udfaf \u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0442\u043e\u0447\u043d\u043e\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n\ud83d\udd27 \u0413\u0438\u0431\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043e\u0434 \u043b\u044e\u0431\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438\n\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043a\u0430\u0442\u0430\u043b\u043e\u0433\u043e\u0432\n\ud83d\udcc8 \u041c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0441\u0442\u044c \u0434\u043b\u044f \u043b\u044e\u0431\u044b\u0445 \u043e\u0431\u044a\u0435\u043c\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445\n\n\u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422 \u0418\u0421\u041f\u041e\u041b\u042c\u0417\u041e\u0412\u0410\u041d\u0418\u042f:\n\ud83d\udcca \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u043c\u0435\u0441\u0442\u043e HTML\n\ud83d\udd04 \u0423\u043d\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0442\u043e\u0432\u0430\u0440\u043e\u0432\n\ud83d\udcbe \u0413\u043e\u0442\u043e\u0432\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u044b\n\u23f0 \u042d\u043a\u043e\u043d\u043e\u043c\u0438\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043d\u0430 \u0440\u0443\u0447\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435\n\n\u0423\u0434\u0430\u0447\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043e\u0439! \ud83d\ude80\n\n---\n\n\u0412\u0435\u0440\u0441\u0438\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438: 1.0\n\u0414\u0430\u0442\u0430: 2025-06-02\n\u0414\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b: \u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 v4.2'''\n\n        # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0441 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c\n        lines = content.split('\\n')\n        \n        for line in lines:\n            line = line.rstrip()\n            \n            # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u0414\u041e \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438\n            current_pos = help_text.index(INSERT)\n            \n            # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0442\u0438\u043f \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0441\u0442\u0438\u043b\u044c\n            if line.startswith('\ud83c\udfaf ') or line.startswith('\ud83d\ude80 ') or line.startswith('\ud83d\udd27 ') or line.startswith('\u2699\ufe0f ') or line.startswith('\ud83d\udca1 ') or line.startswith('\u2328\ufe0f '):\n                # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u0441\u0435\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438\n                if \"\u0431\u044b\u0441\u0442\u0440\u044b\u0439\" in line.lower():\n                    section_positions[\"start\"] = current_pos\n                elif \"\u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f\" in line.lower():\n                    section_positions[\"instruction\"] = current_pos\n                elif \"\u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\" in line.lower():\n                    section_positions[\"settings\"] = current_pos\n                elif \"\u043f\u0440\u0438\u043c\u0435\u0440\u044b\" in line.lower():\n                    section_positions[\"examples\"] = current_pos\n                elif \"\u043f\u0440\u043e\u0431\u043b\u0435\u043c\" in line.lower():\n                    section_positions[\"problems\"] = current_pos\n                elif \"\u043a\u043b\u0430\u0432\u0438\u0448\u0438\" in line.lower():\n                    section_positions[\"hotkeys\"] = current_pos\n                \n                help_text.insert(END, line + '\\n', \"title\")\n                    \n            elif line.startswith('SHA\u0413 ') or line.startswith('\u041f\u0420\u0418\u041c\u0415\u0420 ') or line.startswith('\u041f\u0420\u041e\u0411\u041b\u0415\u041c\u0410:') or line.startswith('\u0420\u0415\u0428\u0415\u041d\u0418\u0415:'):\n                help_text.insert(END, line + '\\n', \"subtitle\")\n            elif line.isupper() and len(line) > 3 and not line.startswith('TOV') and not line.startswith('PHONE'):\n                help_text.insert(END, line + '\\n', \"header\")\n            elif line.startswith('\u2705') or line.startswith('\u2022'):\n                help_text.insert(END, line + '\\n', \"success\")\n            elif line.startswith('\u274c') or line.startswith('\u26a0\ufe0f'):\n                help_text.insert(END, line + '\\n', \"warning\")\n            elif line.startswith('Ctrl+') or line.startswith('F1') or line.startswith('Esc'):\n                help_text.insert(END, line + '\\n', \"code\")\n            elif '\u2192' in line or '<tr>' in line or '<td>' in line:\n                help_text.insert(END, line + '\\n', \"code\")\n            elif line.strip() == '---':\n                help_text.insert(END, '\\n' + '\u2500' * 80 + '\\n\\n', \"emphasis\")\n            else:\n                help_text.insert(END, line + '\\n')\n        \n        help_text.config(state=DISABLED)\n\n    def find_similar_parameters_for_tab(self):\n        \"\"\"\ud83d\udea8 \u042d\u041a\u0421\u0422\u0420\u0410-\u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041d\u042b\u0419 \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (v4.4)\"\"\"\n        if not self.original_values:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0443\")\n            return\n\n        print(\"\\n\ud83d\udd0d \u0417\u0410\u041f\u0423\u0421\u041a \u041f\u041e\u0418\u0421\u041a\u0410 \u041f\u041e\u0425\u041e\u0416\u0418\u0425 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412 (v4.8)\")\n        \n        # \ud83d\udea8 \u041f\u0420\u0418\u041d\u0423\u0414\u0418\u0422\u0415\u041b\u042c\u041d\u0410\u042f \u041e\u0427\u0418\u0421\u0422\u041a\u0410 \u0412\u0421\u0415\u0425 \u0421\u0422\u0410\u0420\u042b\u0425 \u0414\u0410\u041d\u041d\u042b\u0425\n        self.param_merge_settings.clear()\n        print(\"\ud83e\uddf9 \u041e\u0447\u0438\u0449\u0435\u043d\u044b \u0441\u0442\u0430\u0440\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 param_merge_settings\")\n        \n        # \ud83d\udea8 \u0423\u0414\u0410\u041b\u042f\u0415\u041c \u0424\u0410\u0419\u041b \u041d\u0410\u0421\u0422\u0420\u041e\u0415\u041a \u0415\u0421\u041b\u0418 \u0421\u0423\u0429\u0415\u0421\u0422\u0412\u0423\u0415\u0422\n        if os.path.exists(self.PARAM_MERGE_SETTINGS_FILE):\n            try:\n                os.remove(self.PARAM_MERGE_SETTINGS_FILE)\n                print(f\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0435\u043d \u0444\u0430\u0439\u043b \u0441\u0442\u0430\u0440\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a: {self.PARAM_MERGE_SETTINGS_FILE}\")\n            except Exception as e:\n                print(f\"\u26a0\ufe0f \u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a: {e}\")\n        \n        # \ud83d\udea8 \u041f\u0420\u0418\u041d\u0423\u0414\u0418\u0422\u0415\u041b\u042c\u041d\u041e \u041e\u0427\u0418\u0429\u0410\u0415\u041c \u0422\u0410\u0411\u041b\u0418\u0426\u0423\n        for item in self.params_tree.get_children():\n            self.params_tree.delete(item)\n        print(\"\ud83e\uddf9 \u041e\u0447\u0438\u0449\u0435\u043d\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\")\n        \n        # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n        all_params = set()\n        for sku_params in self.original_values.values():\n            all_params.update(sku_params.keys())\n        \n        all_params = list(all_params)\n        print(f\"\ud83d\udccb \u0412\u0441\u0435\u0433\u043e \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {len(all_params)}\")\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0447\u0430\u0441\u0442\u043e\u0442\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        self.update_param_frequency(all_params)\n        \n        print(\"\ud83d\udcca \u0422\u043e\u043f-10 \u0441\u0430\u043c\u044b\u0445 \u0447\u0430\u0441\u0442\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:\")\n        sorted_params = sorted(self.param_frequency.items(), key=lambda x: x[1], reverse=True)[:10]\n        for param, freq in sorted_params:\n            print(f\"  \u2022 {param}: {freq} \u0440\u0430\u0437\")\n        \n        # \ud83d\udd25 \u0422\u0415\u0421\u0422\u0418\u0420\u0423\u0415\u041c \u041f\u0420\u041e\u0411\u041b\u0415\u041c\u041d\u042b\u0415 \u041f\u0410\u0420\u042b \u0414\u041e \u041f\u041e\u0418\u0421\u041a\u0410\n        print(\"\\n\ud83e\uddea \u0422\u0415\u0421\u0422\u0418\u0420\u041e\u0412\u0410\u041d\u0418\u0415 \u041f\u0420\u041e\u0411\u041b\u0415\u041c\u041d\u042b\u0425 \u041f\u0410\u0420:\")\n        test_pairs = [\n            (\"Caption\", \"Stream Authentication\"),\n            (\"Weight\", \"Night\"),\n            (\"IR Distance\", \"Night Vision\"),\n            (\"Operating Humidity\", \"Mounting Bracket\")\n        ]\n        \n        for param1, param2 in test_pairs:\n            if param1 in all_params and param2 in all_params:\n                is_similar = self.are_parameters_truly_similar(param1, param2)\n                print(f\"   \ud83d\udd0d {param1} \u2194 {param2}: {'\u274c \u041e\u0422\u041a\u041b\u041e\u041d\u0415\u041d\u041e' if not is_similar else '\u2705 \u041f\u0420\u0418\u041d\u042f\u0422\u041e'}\")\n        \n        # \ud83d\udd25 \u041d\u0410\u0425\u041e\u0414\u0418\u041c \u041f\u041e\u0425\u041e\u0416\u0418\u0415 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u042b \u0421 \u041d\u041e\u0412\u042b\u041c \u0410\u041b\u0413\u041e\u0420\u0418\u0422\u041c\u041e\u041c\n        print(\"\\n\ud83d\udd0d \u0417\u0410\u041f\u0423\u0421\u041a \u0410\u041b\u0413\u041e\u0420\u0418\u0422\u041c\u0410 \u041f\u041e\u0418\u0421\u041a\u0410...\")\n        similar_groups = self.find_similar_parameters_advanced(all_params)\n        \n        if not similar_groups:\n            print(\"\u274c \u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u041d\u0415 \u041d\u0410\u0419\u0414\u0415\u041d\u042b\")\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u2705 \u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0441 \u0442\u0435\u043a\u0443\u0449\u0438\u043c\u0438 \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c\u0438.\")\n            \n            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n            self.params_tree.insert('', END, values=(\n                \"\", \"\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b\", \"\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u0438\u0437\u0438\u0442\u044c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u0438\", \"\"\n            ))\n            return\n\n        print(f\"\\n\u2705 \u041d\u0410\u0419\u0414\u0415\u041d\u041e \u0413\u0420\u0423\u041f\u041f: {len(similar_groups)}\")\n        \n        # \ud83d\udd25 \u0421\u041e\u0425\u0420\u0410\u041d\u042f\u0415\u041c \u041d\u041e\u0412\u042b\u0415 \u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422\u042b\n        for representative, similar_list in similar_groups.items():\n            # \ud83d\udd25 \u0417\u0410\u0429\u0418\u0422\u0410: \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0435\u0441\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043f\u043e\u043f\u0430\u043b\n            clean_similar_list = [p for p in similar_list if p != representative]\n            \n            if clean_similar_list:  # \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b (\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f)\n                self.param_merge_settings[representative] = {\n                    'similar_params': clean_similar_list,\n                    'selected': False  # \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e\n                }\n                \n                freq = self.param_frequency.get(representative, 0)\n                print(f\"\u2705 \u0421\u041e\u0425\u0420\u0410\u041d\u0415\u041d\u041e: {representative} (\u0447\u0430\u0441\u0442\u043e\u0442\u0430: {freq}) \u2192 {len(clean_similar_list)} \u043f\u043e\u0445\u043e\u0436\u0438\u0445\")\n\n        # \ud83d\udd25 \u0421\u041e\u0425\u0420\u0410\u041d\u042f\u0415\u041c \u0414\u0410\u041d\u041d\u042b\u0415\n        print(f\"\\n\ud83d\udcbe \u0421\u041e\u0425\u0420\u0410\u041d\u0415\u041d\u0418\u0415 \u0414\u0410\u041d\u041d\u042b\u0425...\")\n        self.save_merge_settings()\n        \n        print(f\"\ud83c\udfaf \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0422\u0410\u0411\u041b\u0418\u0426\u042b...\")\n        self.update_params_tree()\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e\n        message = f\"\u2705 \u041d\u0430\u0439\u0434\u0435\u043d\u043e {len(similar_groups)} \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.\\n\"\n        message += \"\ud83d\udcca \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u044b\u0431\u0440\u0430\u043d\u044b \u043f\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 (\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441 \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0438\u043c \u0447\u0438\u0441\u043b\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0433\u043b\u0430\u0432\u043d\u044b\u043c).\\n\"\n        message += \"\u2714\ufe0f \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 '\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432'.\"\n        messagebox.showinfo(\"\u041f\u043e\u0438\u0441\u043a \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", message)\n\n    def update_params_tree(self):\n        \"\"\"\u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u041e\u0415 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u043e\u0439\"\"\"\n        print(\"\ud83c\udfaf \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0422\u0410\u0411\u041b\u0418\u0426\u042b \u041f\u041e\u0425\u041e\u0416\u0418\u0425 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412\")\n        \n        if not self.param_merge_settings:\n            # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n            for item in self.params_tree.get_children():\n                self.params_tree.delete(item)\n            \n            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443\n            self.params_tree.insert('', END, values=(\n                \"\", \"\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b\", \"\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u0441\u043d\u0438\u0437\u0438\u0442\u044c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u0438\", \"\"\n            ))\n            return\n        \n        # \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0443\u044e \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0443 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0443 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e (\u0430\u043b\u0444\u0430\u0432\u0438\u0442)\n        self.params_tree_sort_direction = {}\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 (\u0443\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u0442\u0440\u0435\u043b\u043a\u0438 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438)\n        self.update_params_tree_headers('', '')\n        \n        # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0443 \u043f\u043e \u0430\u043b\u0444\u0430\u0432\u0438\u0442\u0443 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e)\n        print(\"\ud83d\udccb \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 \u043f\u043e \u0430\u043b\u0444\u0430\u0432\u0438\u0442\u0443 (\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e)...\")\n        # \u0423\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u0441\u0442\u0440\u0435\u043b\u043a\u0438\n        self.params_tree_sort_direction['representative'] = 'asc'\n        self.sort_params_tree('representative')\n\n    def on_params_tree_click(self, event):\n        \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043b\u0438\u043a\u0430 \u043f\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        region = self.params_tree.identify(\"region\", event.x, event.y)\n        if region != \"cell\":\n            return\n        col = self.params_tree.identify_column(event.x)\n        row = self.params_tree.identify_row(event.y)\n        \n        if not row or col != '#1':  # \u041a\u043e\u043b\u043e\u043d\u043a\u0430 \"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c\"\n            return\n        \n        values = list(self.params_tree.item(row, 'values'))\n        representative = values[1]\n        \n        # \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\n        if representative in self.param_merge_settings:\n            current_state = self.param_merge_settings[representative].get('selected', False)\n            self.param_merge_settings[representative]['selected'] = not current_state\n            self.save_merge_settings()\n            self.update_params_tree()\n\n    def select_all_params(self):\n        \"\"\"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"\n        for representative in self.param_merge_settings:\n            self.param_merge_settings[representative]['selected'] = True\n        self.save_merge_settings()\n        self.update_params_tree()\n\n    def deselect_all_params(self):\n        \"\"\"\u0421\u043d\u044f\u0442\u044c \u0432\u044b\u0431\u043e\u0440 \u0441\u043e \u0432\u0441\u0435\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        for representative in self.param_merge_settings:\n            self.param_merge_settings[representative]['selected'] = False\n        self.save_merge_settings()\n        self.update_params_tree()\n    \n    def sort_params_tree(self, column):\n        \"\"\"\ud83c\udd95 \u0421\u041e\u0420\u0422\u0418\u0420\u041e\u0412\u041a\u0410 \u041f\u041e \u0421\u0422\u041e\u041b\u0411\u0426\u0410\u041c: \u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043f\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u043c\u0443 \u0441\u0442\u043e\u043b\u0431\u0446\u0443\"\"\"\n        print(f\"\ud83d\udd04 \u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u043f\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u0443: {column}\")\n        \n        # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438\n        current_direction = self.params_tree_sort_direction.get(column, 'asc')\n        new_direction = 'desc' if current_direction == 'asc' else 'asc'\n        self.params_tree_sort_direction[column] = new_direction\n        \n        # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438\n        items_to_sort = []\n        for representative, data in self.param_merge_settings.items():\n            similar_list = data.get('similar_params', [])\n            selected = data.get('selected', False)\n            \n\n            \n            # \ud83d\udd25 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u0418\u0415: similar_list \u0443\u0436\u0435 \u041d\u0415 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f\n            if similar_list:  # \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n                # similar_list \u0443\u0436\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b (\u0431\u0435\u0437 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f)\n                other_params_sorted = sorted(similar_list, key=lambda x: x.lower())\n                similar_str = \"; \".join(other_params_sorted)\n                frequency = self.param_frequency.get(representative, 0)\n                \n                # \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u0440\u0442\u0435\u0436 \u0434\u043b\u044f \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438 (\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435_\u0434\u043b\u044f_\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438, \u0432\u0441\u0435_\u0434\u0430\u043d\u043d\u044b\u0435)\n                if column == 'representative':\n                    sort_value = representative.lower()\n                elif column == 'similar_params':\n                    sort_value = similar_str.lower()\n                elif column == 'frequency':\n                    sort_value = frequency\n                else:\n                    sort_value = representative.lower()\n                \n                items_to_sort.append((sort_value, {\n                    'representative': representative,\n                    'similar_str': similar_str,\n                    'selected': selected,\n                    'frequency': frequency,\n                    'other_params': similar_list  # \ud83d\udd25 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u0418\u0415: \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c similar_list\n                }))\n        \n\n        \n        # \u0421\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c\n        reverse_order = (new_direction == 'desc')\n        items_to_sort.sort(key=lambda x: x[0], reverse=reverse_order)\n        \n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0432 \u043d\u043e\u0432\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435\n        for item in self.params_tree.get_children():\n            self.params_tree.delete(item)\n        \n        direction_symbol = \"\u2193\" if new_direction == 'desc' else \"\u2191\"\n        print(f\"\ud83d\udcca \u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 {len(items_to_sort)} \u0433\u0440\u0443\u043f\u043f \u043f\u043e {column} {direction_symbol}\")\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u043a\u0430\u0437\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438\n        self.update_params_tree_headers(column, direction_symbol)\n        \n        for sort_value, item_data in items_to_sort:\n            merge_symbol = \"\u2611\" if item_data['selected'] else \"\u2610\"\n            freq_info = f\"{item_data['frequency']}\"\n            \n            if len(item_data['other_params']) > 0:\n                other_freqs = [self.param_frequency.get(p, 0) for p in item_data['other_params']]\n                max_other_freq = max(other_freqs) if other_freqs else 0\n                freq_info += f\" (\u043c\u0430\u043a\u0441.\u0434\u0440: {max_other_freq})\"\n            \n            self.params_tree.insert('', END, values=(\n                merge_symbol, item_data['representative'], item_data['similar_str'], freq_info\n            ))\n        \n        print(f\"\u2705 \u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430: {new_direction} \u043f\u043e {column}\")\n    \n    def update_params_tree_headers(self, sorted_column, direction_symbol):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0441 \u0438\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u0435\u0439 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0438\"\"\"\n        # \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438\n        self.params_tree.heading('merge', text='\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c')\n        self.params_tree.heading('representative', \n                                 text=f'\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 {\"\" if sorted_column != \"representative\" else direction_symbol}', \n                                 command=lambda: self.sort_params_tree('representative'))\n        self.params_tree.heading('similar_params', \n                                 text=f'\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b {\"\" if sorted_column != \"similar_params\" else direction_symbol}', \n                                 command=lambda: self.sort_params_tree('similar_params'))\n        self.params_tree.heading('frequency', \n                                 text=f'\u0427\u0430\u0441\u0442\u043e\u0442\u0430 {\"\" if sorted_column != \"frequency\" else direction_symbol}', \n                                 command=lambda: self.sort_params_tree('frequency'))\n\n    def apply_param_merges_from_tab(self):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        merge_groups = {}\n        for representative, data in self.param_merge_settings.items():\n            if data.get('selected', False):\n                merge_groups[representative] = data['similar_params']\n        \n        if not merge_groups:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\")\n            return\n\n        self.apply_parameter_merges(merge_groups)\n        messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u043e {len(merge_groups)} \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\")\n\n    def clear_param_merge_settings(self):\n        \"\"\"\u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432?\"):\n            self.param_merge_settings.clear()\n            self.save_merge_settings()\n            self.update_params_tree()\n\n    def find_similar_values_for_tab(self):\n        \"\"\"\u041f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u0432\u043a\u043b\u0430\u0434\u043a\u0438\"\"\"\n        if not self.original_values:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0443\")\n            return\n\n        similar_values_data = self.find_similar_values_in_all_parameters()\n        \n        if not similar_values_data:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b\")\n            return\n\n        # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n        for param_name, groups in similar_values_data.items():\n            if param_name not in self.value_merge_settings:\n                self.value_merge_settings[param_name] = {}\n            \n            for representative, similar_list in groups.items():\n                if len(similar_list) > 1:\n                    self.value_merge_settings[param_name][representative] = {\n                        'similar_values': similar_list,\n                        'selected': False\n                    }\n\n        self.save_merge_settings()\n        self.update_values_tree()\n\n    def update_values_tree(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n        for item in self.values_tree.get_children():\n            self.values_tree.delete(item)\n\n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u043c\u0438\n        for param_name, groups in self.value_merge_settings.items():\n            for representative, data in groups.items():\n                similar_list = data.get('similar_values', [])\n                selected = data.get('selected', False)\n                \n                if len(similar_list) > 1:\n                    similar_str = \"; \".join(similar_list)\n                    merge_symbol = \"\u2611\" if selected else \"\u2610\"\n                    frequency = len(similar_list)\n                    \n                    self.values_tree.insert('', END, values=(\n                        merge_symbol, param_name, representative, similar_str, frequency\n                    ))\n\n    def on_values_tree_click(self, event):\n        \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043b\u0438\u043a\u0430 \u043f\u043e \u0442\u0430\u0431\u043b\u0438\u0446\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        region = self.values_tree.identify(\"region\", event.x, event.y)\n        if region != \"cell\":\n            return\n        col = self.values_tree.identify_column(event.x)\n        row = self.values_tree.identify_row(event.y)\n        \n        if not row or col != '#1':  # \u041a\u043e\u043b\u043e\u043d\u043a\u0430 \"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c\"\n            return\n        \n        values = list(self.values_tree.item(row, 'values'))\n        param_name = values[1]\n        representative = values[2]\n        \n        # \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\n        if param_name in self.value_merge_settings and representative in self.value_merge_settings[param_name]:\n            current_state = self.value_merge_settings[param_name][representative].get('selected', False)\n            self.value_merge_settings[param_name][representative]['selected'] = not current_state\n            self.save_merge_settings()\n            self.update_values_tree()\n\n    def select_all_values(self):\n        \"\"\"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"\n        for param_name in self.value_merge_settings:\n            for representative in self.value_merge_settings[param_name]:\n                self.value_merge_settings[param_name][representative]['selected'] = True\n        self.save_merge_settings()\n        self.update_values_tree()\n\n    def deselect_all_values(self):\n        \"\"\"\u0421\u043d\u044f\u0442\u044c \u0432\u044b\u0431\u043e\u0440 \u0441\u043e \u0432\u0441\u0435\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        for param_name in self.value_merge_settings:\n            for representative in self.value_merge_settings[param_name]:\n                self.value_merge_settings[param_name][representative]['selected'] = False\n        self.save_merge_settings()\n        self.update_values_tree()\n\n    def apply_value_merges_from_tab(self):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        merge_data = {}\n        total_merges = 0\n        \n        for param_name, groups in self.value_merge_settings.items():\n            param_merges = {}\n            for representative, data in groups.items():\n                if data.get('selected', False):\n                    param_merges[representative] = data['similar_values']\n                    total_merges += 1\n            \n            if param_merges:\n                merge_data[param_name] = param_merges\n        \n        if not merge_data:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041d\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043e \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\")\n            return\n\n        self.apply_value_merges(merge_data)\n        messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u043e {total_merges} \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\")\n\n    def clear_value_merge_settings(self):\n        \"\"\"\u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u041e\u0447\u0438\u0441\u0442\u0438\u0442\u044c \u0432\u0441\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439?\"):\n            self.value_merge_settings.clear()\n            self.save_merge_settings()\n            self.update_values_tree()\n\n    def load_settings(self):\n        if os.path.exists(self.SETTINGS_FILE):\n            try:\n                with open(self.SETTINGS_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.settings = json.load(f)\n            except Exception as e:\n                messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438: {str(e)}\")\n                self.settings = {}\n        else:\n            self.settings = {}\n        \n        # \u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043a\u043b\u044e\u0447\u0438 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442\n        if \"extra_cols\" not in self.settings:\n            self.settings[\"extra_cols\"] = []\n        if \"features_col\" not in self.settings:\n            self.settings[\"features_col\"] = \"\"\n        if \"source_file_path\" not in self.settings:\n            self.settings[\"source_file_path\"] = \"\"\n        if \"output_folder_path\" not in self.settings:\n            self.settings[\"output_folder_path\"] = \"\"\n\n    def save_settings(self):\n        self.settings[\"features_col\"] = self.features_col.get()\n        # extra_cols \u0443\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0432 settings, \u043d\u0435 \u043f\u0430\u0440\u0441\u0438\u043c \u0438\u0437 \u043b\u0435\u0439\u0431\u043b\u0430\n        if \"extra_cols\" not in self.settings:\n            # Fallback: \u0435\u0441\u043b\u0438 \u043f\u043e \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 extra_cols \u043d\u0435\u0442 \u0432 settings, \u0431\u0435\u0440\u0435\u043c \u0438\u0437 \u043b\u0435\u0439\u0431\u043b\u0430\n            label_text = self.extra_cols_label.cget(\"text\")\n            self.settings[\"extra_cols\"] = [col.strip() for col in label_text.split(\",\") if col.strip()] if label_text else []\n        self.settings[\"manual_split_values\"] = self.manual_split_values\n        self.settings[\"source_file_path\"] = self.source_file_var.get()\n        self.settings[\"output_folder_path\"] = self.output_folder_var.get()\n\n        try:\n            with open(self.SETTINGS_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.settings, f, ensure_ascii=False, indent=2)\n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438: {str(e)}\")\n\n    def browse_source_file(self):\n        \"\"\"\n        \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430\n        \"\"\"\n        current_path = self.source_file_var.get()\n        initial_dir = os.path.dirname(current_path) if current_path and os.path.exists(current_path) else os.getcwd()\n        \n        filename = filedialog.askopenfilename(\n            title=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 Excel \u0444\u0430\u0439\u043b\",\n            initialdir=initial_dir,\n            filetypes=[(\"Excel files\", \"*.xlsx *.xls\"), (\"All files\", \"*.*\")]\n        )\n        \n        if filename:\n            self.source_file_var.set(filename)\n            self.save_settings()\n\n    def browse_output_folder(self):\n        \"\"\"\n        \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043f\u0430\u043f\u043a\u0438 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\n        \"\"\"\n        current_path = self.output_folder_var.get()\n        initial_dir = current_path if current_path and os.path.exists(current_path) else os.getcwd()\n        \n        folder = filedialog.askdirectory(\n            title=\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u043f\u043a\u0443 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\",\n            initialdir=initial_dir\n        )\n        \n        if folder:\n            self.output_folder_var.set(folder)\n            self.save_settings()\n\n    def load_file_from_settings(self):\n        \"\"\"\n        \u0411\u044b\u0441\u0442\u0440\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0444\u0430\u0439\u043b\u0430 \u0438\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\n        \"\"\"\n        source_path = self.settings.get(\"source_file_path\", \"\")\n        \n        if not source_path:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0412 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d \u043f\u0443\u0442\u044c \u043a \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u043c\u0443 \u0444\u0430\u0439\u043b\u0443\")\n            return\n        \n        if not os.path.exists(source_path):\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d: {source_path}\")\n            return\n        \n        try:\n            self.load_excel_file(source_path)\n            messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0424\u0430\u0439\u043b \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d: {os.path.basename(source_path)}\")\n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b: {str(e)}\")\n\n    def show_extra_cols_dialog(self):\n        if not hasattr(self, 'df_columns') or not self.df_columns:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0444\u0430\u0439\u043b\")\n            return\n        \n        # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b\n        current_selection = self.settings.get(\"extra_cols\", [])\n        \n        dialog = ColumnSelectorDialog(self.root, self.df_columns, current_selection)\n        self.root.wait_window(dialog.dialog)\n        \n        if dialog.result is not None:\n            # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n            self.settings[\"extra_cols\"] = dialog.result\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\n            display_text = \", \".join(dialog.result) if dialog.result else \"\"\n            self.extra_cols_label.config(text=display_text)\n            \n            # \u0412\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u044f\n            col_count = len(dialog.result)\n            if col_count > 0:\n                self.extra_cols_label.config(bg=\"lightgreen\")\n                ToolTip(self.extra_cols_label, f\"\u0412\u044b\u0431\u0440\u0430\u043d\u043e {col_count} \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\")\n            else:\n                self.extra_cols_label.config(bg=\"SystemButtonFace\")\n            \n            # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0432 \u0444\u0430\u0439\u043b\n            self.save_settings()\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0442\u0443\u0441\n            self.info_label.config(text=f\"\u0412\u044b\u0431\u0440\u0430\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432: {col_count}\")\n\n    def load_file(self):\n        # \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u043f\u0443\u0442\u044c \u0432 \u043f\u043e\u043b\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430\n        source_path = self.source_file_var.get().strip()\n        \n        if source_path and os.path.exists(source_path):\n            # \u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c \u0444\u0430\u0439\u043b \u0438\u0437 \u043f\u043e\u043b\u044f\n            try:\n                self.load_excel_file(source_path)\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0424\u0430\u0439\u043b \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d: {os.path.basename(source_path)}\")\n                return\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0438\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a: {str(e)}\")\n        \n        # \u0415\u0441\u043b\u0438 \u0444\u0430\u0439\u043b\u0430 \u043d\u0435\u0442 \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 - \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        filename = filedialog.askopenfilename(filetypes=[(\"Excel files\", \"*.xlsx *.xls\")])\n        if filename:\n            try:\n                self.load_excel_file(filename)\n                # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430\n                self.source_file_var.set(filename)\n                self.save_settings()\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0424\u0430\u0439\u043b \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d: {os.path.basename(filename)}\")\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b: {str(e)}\")\n\n    def load_excel_file(self, filename):\n        df = pd.read_excel(filename)\n        self.original_df = df.copy()\n        self.df_columns = df.columns.tolist()\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0443 \u0434\u043b\u044f \u043f\u043e\u043b\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\n        if hasattr(self, 'features_col'):\n            tooltip_text = f\"\u041d\u043e\u043c\u0435\u0440 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0441 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438 (\u043e\u0442\u0441\u0447\u0451\u0442 \u0441 1)\\n\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b: {', '.join([f'{i+1}. {col}' for i, col in enumerate(self.df_columns)])}\"\n            ToolTip(self.features_col, tooltip_text)\n\n    def process_table(self):\n        if self.original_df is None:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0444\u0430\u0439\u043b\")\n            return\n\n        try:\n            col_idx = int(self.features_col.get()) - 1\n            html_col = self.original_df.columns[col_idx]\n        except Exception:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u041d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 \u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\")\n            return\n\n        self.param_mapping = {}\n        self.original_values = {}\n\n        if \"manual_split_values\" in self.settings and isinstance(self.settings[\"manual_split_values\"], dict):\n            self.manual_split_values = self.settings[\"manual_split_values\"].copy()\n        else:\n            self.manual_split_values = {}\n\n        self.manual_split_results = {}\n\n        for idx, row in self.original_df.iterrows():\n            sku = row.get(\"\u0410\u0440\u0442\u0438\u043a\u0443\u043b\", None)\n            if sku is None:\n                continue\n            html = str(row[html_col])\n            params = self.parse_html_table(html)\n            self.original_values[sku] = {}\n            for param, value in params.items():\n                clean_value = self.remove_duplicates_from_cell(value)\n                self.original_values[sku][param] = clean_value\n\n        all_params = set()\n        for sku in self.original_df[\"\u0410\u0440\u0442\u0438\u043a\u0443\u043b\"].unique():\n            for param in self.original_values.get(sku, {}):\n                all_params.add(param)\n\n        self.update_mappings_from_params(all_params)\n        # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432\u043c\u0435\u0441\u0442\u043e \u0441\u0442\u0430\u0440\u043e\u0439\n        print(\"\ud83d\udd04 \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u041d\u041e\u0412\u0423\u042e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0434\u043b\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u044f...\")\n        similar_groups_dict = self.find_similar_parameters_advanced(list(all_params))\n        \n        # \u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0444\u043e\u0440\u043c\u0430\u0442 \u0434\u043b\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u044f  \n        self.similar_groups = {}\n        group_id = 1\n        for representative, group_params in similar_groups_dict.items():\n            for param in group_params:\n                self.similar_groups[param] = group_id\n            group_id += 1\n        self.tree.delete(*self.tree.get_children())\n\n        rownum = 1\n        self.case_value_variants = defaultdict(set)\n\n        for param in sorted(all_params):\n            group_id = self.similar_groups.get(param, 0)\n            orig_values_set = set()\n            for sku in self.original_df[\"\u0410\u0440\u0442\u0438\u043a\u0443\u043b\"].unique():\n                vals = self.original_values.get(sku, {})\n                if param in vals:\n                    orig_values_set.add(vals[param])\n\n            norm_variants = set()\n            for val in orig_values_set:\n                norm_val = normalize_variant(val)\n                norm_variants.add(norm_val)\n                self.case_value_variants[norm_val.lower()].add(val)\n\n            orig_values_joined = \"; \".join(norm_variants)\n\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041d\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\n            orig_values_processed = self.apply_case_normalization_in_str(orig_values_joined, delimiter=';')\n            orig_values_processed = self.remove_duplicates_from_list([p.strip() for p in orig_values_processed.split(\";\") if p.strip()])\n            orig_values = \"; \".join(orig_values_processed)\n            orig_values = self.remove_duplicates_from_cell(orig_values)\n\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 + \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n            \n            # 1. \u0418\u0421\u0425\u041e\u0414\u041d\u042b\u0415 \u0414\u0410\u041d\u041d\u042b\u0415 (\u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430) \u0434\u043b\u044f \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\" \u0438 \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\"\n            original_param = param  # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c\n            original_values = orig_values  # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c\n            \n            # 2. \u041f\u0420\u0418\u041c\u0415\u041d\u042f\u0415\u041c \u0421\u041e\u041f\u041e\u0421\u0422\u0410\u0412\u041b\u0415\u041d\u0418\u042f \u0418 \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u042b \u0434\u043b\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\n            # \u0428\u0430\u0433 1: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b (\u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c)\n            mapped_param = self.mappings.get(param, param)\n            if mapped_param != param:\n                print(f\"\ud83d\udccb \u0421\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435: '{param}' \u2192 '{mapped_param}'\")\n            \n            # \u0428\u0430\u0433 2: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043a \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0443 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n            new_param = self.apply_auto_translation(mapped_param, is_parameter=True)\n            \n            delimiter = \";\"\n            if param in self.manual_split_values and isinstance(self.manual_split_values[param], str) and self.manual_split_values[param] != False:\n                delimiter = self.manual_split_values[param]\n            \n            # \ud83c\udd95 \u041a\u0420\u0418\u0422\u0418\u0427\u0415\u0421\u041a\u0410\u042f \u0417\u0410\u0429\u0418\u0422\u0410 \u041e\u0422 \u041f\u0423\u0421\u0422\u041e\u0413\u041e \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n            if not delimiter:\n                delimiter = \";\"\n                print(f\"\u26a0\ufe0f \u0410\u0412\u0410\u0420\u0418\u0419\u041d\u0410\u042f \u0417\u0410\u041c\u0415\u041d\u0410 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f \u0434\u043b\u044f '{param}': \u043f\u0443\u0441\u0442\u043e\u0439 \u2192 ';'\")\n\n            # 3. \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0410 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419: \u0411\u0415\u0417 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b (\u043e\u043d\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432!)\n            split_result_list = self.split_values(orig_values, delimiter)\n            print(f\"\ud83d\udd27 DEBUG {param}: 1.\u041f\u043e\u0441\u043b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f: {split_result_list}\")\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041d\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c, \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n            split_result_list = [self.apply_case_normalization(v) for v in split_result_list]\n            print(f\"\ud83d\udd27 DEBUG {param}: 2.\u041f\u043e\u0441\u043b\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438: {split_result_list}\")\n            \n            split_result_list = self.remove_duplicates_from_list(split_result_list)\n            print(f\"\ud83d\udd27 DEBUG {param}: 3.\u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0439 \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438: {split_result_list}\")\n\n            # \ud83c\udd95 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\n            split_result_list = [self.restore_protected_values_in_string(v) for v in split_result_list]\n            print(f\"\ud83d\udd27 DEBUG {param}: 4.\u041f\u043e\u0441\u043b\u0435 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f: {split_result_list}\")\n            \n            # \ud83c\udd95 \u041f\u0420\u0418\u041c\u0415\u041d\u042f\u0415\u041c \u041f\u0420\u0410\u0412\u0418\u041b\u0410 \u0417\u0410\u041c\u0415\u041d\u042b\n            split_result_list = [self.apply_replacement_rules(v) for v in split_result_list]\n            print(f\"\ud83d\udd27 DEBUG {param}: 5.\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u0437\u0430\u043c\u0435\u043d\u044b: {split_result_list}\")\n            \n            # \ud83c\udd95 \u041f\u0420\u0418\u041c\u0415\u041d\u042f\u0415\u041c \u0421\u0418\u041d\u041e\u041d\u0418\u041c\u042b \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \n            split_result_list = self.synonyms_manager.merge_synonyms_in_list(split_result_list)\n            print(f\"\ud83d\udd27 DEBUG {param}: 6.\u041f\u043e\u0441\u043b\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {split_result_list}\")\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b \u041f\u041e\u0421\u041b\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n            split_result_list = self.remove_duplicates_from_list(split_result_list)\n            print(f\"\ud83d\udd27 DEBUG {param}: 7.\u041f\u043e\u0441\u043b\u0435 \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {split_result_list}\")\n            \n            # \ud83c\udd95 \u041f\u0420\u0418\u041c\u0415\u041d\u042f\u0415\u041c \u041f\u0420\u0410\u0412\u0418\u041b\u0410 \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u0410 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419\n            split_result_list = self.apply_value_translation_rules(split_result_list)\n            print(f\"\ud83d\udd27 DEBUG {param}: 8.\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {split_result_list}\")\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041d\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c (\u0442\u043e\u043b\u044c\u043a\u043e \u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c!)\n            # \u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u043b \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0442\u0438\u043f\u0430 \"Audio in\" \u2192 \"\u0410\u0443\u0434\u0438\u043e in\"\n            # \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 \u0437\u0430\u043c\u0435\u043d\u044b \u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\u043c\u0438\n            \n            # \ud83d\udd27 \u0424\u0418\u041d\u0410\u041b\u042c\u041d\u0410\u042f \u0414\u0415\u0414\u0423\u041f\u041b\u0418\u041a\u0410\u0426\u0418\u042f (\u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439)\n            split_result_list = self.remove_duplicates_from_list(split_result_list)\n            print(f\"\ud83d\udd27 DEBUG {param}: 9.\u0424\u0418\u041d\u0410\u041b\u042c\u041d\u0410\u042f \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f: {split_result_list}\")\n            \n            split_result_str = \"; \".join(split_result_list)\n            print(f\"\ud83d\udd27 DEBUG {param}: 10.\u0418\u0422\u041e\u0413\u041e\u0412\u042b\u0419 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{split_result_str}'\")\n\n            # 4. \u0417\u0410\u041f\u041e\u041b\u041d\u042f\u0415\u041c \u0422\u0410\u0411\u041b\u0418\u0426\u0423: \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 + \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b\n            self.tree.insert(\"\", \"end\", values=(\n                rownum, original_param, original_values, new_param, delimiter, split_result_str\n            ), tags=(f\"group_{group_id}\",))\n            rownum += 1\n\n        self.df = pd.DataFrame([{\n            \"\u2116\": int(values[0]),\n            \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\": values[1],\n            \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\": values[2],\n            \"\u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\": values[3],\n            \"\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c\": values[4],\n            \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\": values[5]\n        } for item in self.tree.get_children() for values in [self.tree.item(item, \"values\")]])\n\n        # \ud83c\udd95 \u0426\u0412\u0415\u0422\u041e\u0412\u041e\u0415 \u0412\u042b\u0414\u0415\u041b\u0415\u041d\u0418\u0415 \u041f\u041e\u0425\u041e\u0416\u0418\u0425 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412 v4.0\n        self.refresh_treeview_with_colors()\n        \n        # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        duplicates_cleaned = self.auto_clean_duplicate_values()\n        \n        # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        self.auto_suggest_parameter_merge()\n        \n        # \ud83c\udd95 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0430 (\u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u043e \u0438\u0437 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043a\u043d\u043e\u043f\u043a\u0438)\n        print(\"\ud83d\udd04 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0430...\")\n        self.normalize_all_values_format(show_message=False)\n        \n        # \ud83c\udd95 \u0410\u0412\u0422\u041e\u041c\u0410\u0422\u0418\u0427\u0415\u0421\u041a\u0418\u0419 \u0410\u041d\u0410\u041b\u0418\u0417 \u0415\u0414\u0418\u041d\u0418\u0426 \u0418\u0417\u041c\u0415\u0420\u0415\u041d\u0418\u042f\n        print(\"\ud83d\udcca \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0430\u043d\u0430\u043b\u0438\u0437 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f...\")\n        units_added = self.auto_analyze_units_and_create_rules()\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f\u0430\u0445\n        similar_count = len(self.similar_param_groups)\n        message = f\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430. \u041d\u0430\u0439\u0434\u0435\u043d\u043e {len(all_params)} \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432.\"\n        if similar_count > 0:\n            message += f\"\\n\ud83c\udfa8 \u041d\u0430\u0439\u0434\u0435\u043d\u043e {similar_count} \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 (\u0432\u044b\u0434\u0435\u043b\u0435\u043d\u044b \u0446\u0432\u0435\u0442\u043e\u043c).\"\n        message += f\"\\n\u2705 \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e.\"\n        if units_added > 0:\n            message += f\"\\n\ud83d\udcca \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e {units_added} \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f.\"\n        \n        messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", message)\n\n    def auto_analyze_units_and_create_rules(self):\n        \"\"\"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0430\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u0442 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u044b\"\"\"\n        try:\n            if not hasattr(self, 'tree') or not self.tree.get_children():\n                return 0\n            \n            # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\n            all_values = []\n            for item in self.tree.get_children():\n                values = self.tree.item(item, 'values')\n                if len(values) >= 6:  # \u0423\u0431\u0435\u0436\u0434\u0430\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\n                    split_results = values[5]  # \u0421\u0442\u043e\u043b\u0431\u0435\u0446 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\n                    if split_results:\n                        # \u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f\u043c\n                        split_values = [v.strip() for v in re.split(r'[;,|]', split_results) if v.strip()]\n                        all_values.extend(split_values)\n            \n            if not all_values:\n                print(\"\ud83d\udcca \u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\")\n                return 0\n            \n            # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0434\u043b\u044f \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n            unit_rules = self.create_unit_conversion_rules(all_values)\n            \n            if not unit_rules:\n                print(\"\ud83d\udcca \u0415\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b\")\n                return 0\n            \n            # \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n            added_count = 0\n            for rule in unit_rules:\n                # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043b\u0438 \u0443\u0436\u0435 \u0442\u0430\u043a\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e\n                existing = any(r['name'] == rule['name'] for r in self.replacement_rules)\n                if not existing:\n                    self.replacement_rules.append({\n                        'name': rule['name'],\n                        'pattern': rule['pattern'],\n                        'replacement': rule['replacement'],\n                        'active': True\n                    })\n                    added_count += 1\n                    print(f\"\u2795 \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u043e: {rule['name']}\")\n            \n            if added_count > 0:\n                # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n                self.save_replacement_rules(self.replacement_rules)\n                print(f\"\u2705 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e {added_count} \u043f\u0440\u0430\u0432\u0438\u043b \u0434\u043b\u044f \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\")\n            \n            return added_count\n            \n        except Exception as e:\n            print(f\"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0430\u043d\u0430\u043b\u0438\u0437\u0435 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f: {e}\")\n            return 0\n    \n    def create_unit_conversion_rules(self, values):\n        \"\"\"\u0421\u043e\u0437\u0434\u0430\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (\u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043b\u044f IP-\u0432\u0438\u0434\u0435\u043e\u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f)\"\"\"\n        unit_patterns = {\n            # === \u0424\u0418\u0417\u0418\u0427\u0415\u0421\u041a\u0418\u0415 \u0420\u0410\u0417\u041c\u0415\u0420\u042b ===\n            'inch': ('\u0434\u044e\u0439\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\"\\\\b'),\n            'inches': ('\u0434\u044e\u0439\u043c\u043e\u0432', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*inches?\\\\b'),\n            'ft': ('\u0444\u0443\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*ft\\\\b'),\n            'feet': ('\u0444\u0443\u0442\u043e\u0432', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*feet\\\\b'),\n            'mm': ('\u043c\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mm\\\\b'),\n            'cm': ('\u0441\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*cm\\\\b'),\n            'meter': ('\u043c\u0435\u0442\u0440', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*meters?\\\\b'),\n            'm': ('\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*m\\\\b'),\n            '\u03bcm': ('\u043c\u043a\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*\u03bcm\\\\b'),\n            'nm': ('\u043d\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*nm\\\\b'),\n            \n            # === \u0412\u0415\u0421 ===\n            'kg': ('\u043a\u0433', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kg\\\\b'),\n            'gram': ('\u0433\u0440\u0430\u043c\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*grams?\\\\b'),\n            'g': ('\u0433', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*g\\\\b'),\n            'lb': ('\u0444\u0443\u043d\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*lbs?\\\\b'),\n            'oz': ('\u0443\u043d\u0446\u0438\u044f', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*oz\\\\b'),\n            \n            # === \u042d\u041b\u0415\u041a\u0422\u0420\u0418\u0427\u0415\u0421\u041a\u0418\u0415 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u042b ===\n            'volt': ('\u0432\u043e\u043b\u044c\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*V\\\\b'),\n            'v': ('\u0412', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*V\\\\b'),\n            'mv': ('\u043c\u0412', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mV\\\\b'),\n            'kv': ('\u043a\u0412', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kV\\\\b'),\n            'amp': ('\u0430\u043c\u043f\u0435\u0440', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*A\\\\b'),\n            'a': ('\u0410', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*A\\\\b'),\n            'ma': ('\u043c\u0410', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mA\\\\b'),\n            '\u03bca': ('\u043c\u043a\u0410', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*\u03bcA\\\\b'),\n            'watt': ('\u0432\u0430\u0442\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*W\\\\b'),\n            'w': ('\u0412\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*W\\\\b'),\n            'mw': ('\u043c\u0412\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mW\\\\b'),\n            'kw': ('\u043a\u0412\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kW\\\\b'),\n            'wh': ('\u0412\u0442\u22c5\u0447', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Wh\\\\b'),\n            'kwh': ('\u043a\u0412\u0442\u22c5\u0447', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kWh\\\\b'),\n            'mah': ('\u043c\u0410\u0447', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mAh\\\\b'),\n            'ohm': ('\u041e\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Ohm\\\\b'),\n            'kohm': ('\u043a\u041e\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kOhm\\\\b'),\n            \n            # === \u0427\u0410\u0421\u0422\u041e\u0422\u0410 \u0418 \u0412\u0420\u0415\u041c\u042f ===\n            'hz': ('\u0413\u0446', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Hz\\\\b'),\n            'khz': ('\u043a\u0413\u0446', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kHz\\\\b'),\n            'mhz': ('\u041c\u0413\u0446', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*MHz\\\\b'),\n            'ghz': ('\u0413\u0413\u0446', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*GHz\\\\b'),\n            'fps': ('\u043a\u0430\u0434\u0440/\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*fps\\\\b'),\n            'sec': ('\u0441\u0435\u043a', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*sec\\\\b'),\n            's': ('\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*s\\\\b'),\n            'ms': ('\u043c\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*ms\\\\b'),\n            '\u03bcs': ('\u043c\u043a\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*\u03bcs\\\\b'),\n            'ns': ('\u043d\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*ns\\\\b'),\n            'min': ('\u043c\u0438\u043d', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*min\\\\b'),\n            'hour': ('\u0447\u0430\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*hours?\\\\b'),\n            'h': ('\u0447', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*h\\\\b'),\n            'day': ('\u0434\u0435\u043d\u044c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*days?\\\\b'),\n            \n            # === \u041f\u0410\u041c\u042f\u0422\u042c \u0418 \u0414\u0410\u041d\u041d\u042b\u0415 ===\n            'b': ('\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*B\\\\b'),\n            'byte': ('\u0431\u0430\u0439\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*bytes?\\\\b'),\n            'kb': ('\u041a\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*KB\\\\b'),\n            'mb': ('\u041c\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*MB\\\\b'),\n            'gb': ('\u0413\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*GB\\\\b'),\n            'tb': ('\u0422\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*TB\\\\b'),\n            'pb': ('\u041f\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*PB\\\\b'),\n            'eb': ('\u042d\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*EB\\\\b'),\n            \n            # === \u0421\u041a\u041e\u0420\u041e\u0421\u0422\u042c \u041f\u0415\u0420\u0415\u0414\u0410\u0427\u0418 \u0414\u0410\u041d\u041d\u042b\u0425 ===\n            'bps': ('\u0431\u0438\u0442/\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*bps\\\\b'),\n            'kbps': ('\u043a\u0431\u0438\u0442/\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kbps\\\\b'),\n            'mbps': ('\u041c\u0431\u0438\u0442/\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Mbps\\\\b'),\n            'gbps': ('\u0413\u0431\u0438\u0442/\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Gbps\\\\b'),\n            'tbps': ('\u0422\u0431\u0438\u0442/\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Tbps\\\\b'),\n            \n            # === \u0418\u0417\u041e\u0411\u0420\u0410\u0416\u0415\u041d\u0418\u042f \u0418 \u0412\u0418\u0414\u0415\u041e ===\n            'mp': ('\u043c\u043f', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mp\\\\b'),\n            'megapixel': ('\u043c\u0435\u0433\u0430\u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*megapixels?\\\\b'),\n            'px': ('\u043f\u0438\u043a\u0441', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*px\\\\b'),\n            'pixel': ('\u043f\u0438\u043a\u0441\u0435\u043b\u044c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*pixels?\\\\b'),\n            'dpi': ('\u0442\u043e\u0447\u0435\u043a/\u0434\u044e\u0439\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dpi\\\\b'),\n            'ppi': ('\u043f\u0438\u043a\u0441/\u0434\u044e\u0439\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*ppi\\\\b'),\n            'lpi': ('\u043b\u0438\u043d\u0438\u0439/\u0434\u044e\u0439\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*lpi\\\\b'),\n            \n            # === \u0417\u0412\u0423\u041a \u0418 \u041e\u0421\u0412\u0415\u0429\u0415\u041d\u041d\u041e\u0421\u0422\u042c ===\n            'db': ('\u0434\u0411', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dB\\\\b'),\n            'dba': ('\u0434\u0411\u0410', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dBA\\\\b'),\n            'dbc': ('\u0434\u0411\u0421', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dBC\\\\b'),\n            'dbi': ('\u0434\u0411\u0438', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dBi\\\\b'),\n            'dbm': ('\u0434\u0411\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dBm\\\\b'),\n            'dbw': ('\u0434\u0411\u0412\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dBW\\\\b'),\n            'dbfs': ('\u0434\u0411FS', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*dBFS\\\\b'),\n            'lux': ('\u043b\u043a', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*lux\\\\b'),\n            'lm': ('\u043b\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*lm\\\\b'),\n            'cd': ('\u043a\u0434', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*cd\\\\b'),\n            'cd/m\u00b2': ('\u043a\u0434/\u043c\u00b2', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*cd/m\u00b2\\\\b'),\n            'nit': ('\u043d\u0438\u0442', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*nit\\\\b'),\n            \n            # === \u0422\u0415\u041c\u041f\u0415\u0420\u0410\u0422\u0423\u0420\u0410 (\u0442\u043e\u043b\u044c\u043a\u043e \u0426\u0435\u043b\u044c\u0441\u0438\u0439!) ===\n            'celsius': ('\u00b0C', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*\u00b0?C\\\\b'),\n            'kelvin': ('\u041a', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*K\\\\b'),\n            \n            # === \u0414\u0410\u0412\u041b\u0415\u041d\u0418\u0415 \u0418 \u0412\u041b\u0410\u0416\u041d\u041e\u0421\u0422\u042c ===\n            'pa': ('\u041f\u0430', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*Pa\\\\b'),\n            'kpa': ('\u043a\u041f\u0430', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*kPa\\\\b'),\n            'mpa': ('\u041c\u041f\u0430', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*MPa\\\\b'),\n            'bar': ('\u0431\u0430\u0440', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*bar\\\\b'),\n            'mbar': ('\u043c\u0431\u0430\u0440', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mbar\\\\b'),\n            'psi': ('\u0444\u0443\u043d\u0442/\u043a\u0432.\u0434\u044e\u0439\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*psi\\\\b'),\n            'atm': ('\u0430\u0442\u043c', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*atm\\\\b'),\n            'mmhg': ('\u043c\u043c \u0440\u0442.\u0441\u0442.', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*mmHg\\\\b'),\n            'rh': ('%\u041e\u0412', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*%?RH\\\\b'),\n            \n            # === \u0423\u0413\u041b\u042b \u0418 \u0413\u0415\u041e\u041c\u0415\u0422\u0420\u0418\u042f ===\n            'degree': ('\u00b0', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*degrees?\\\\b'),\n            'fov': ('\u043f\u043e\u043b\u0435 \u0437\u0440\u0435\u043d\u0438\u044f', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*FOV\\\\b'),\n            'aov': ('\u0443\u0433\u043e\u043b \u043e\u0431\u0437\u043e\u0440\u0430', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*AOV\\\\b'),\n            'hfov': ('\u0433\u043e\u0440\u0438\u0437. \u043e\u0431\u0437\u043e\u0440', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*HFOV\\\\b'),\n            'vfov': ('\u0432\u0435\u0440\u0442. \u043e\u0431\u0437\u043e\u0440', r'\\\\b(\\\\d+(?:\\\\.\\\\d+)?)\\\\s*VFOV\\\\b'),\n            \n            # === \u041f\u0420\u041e\u0421\u0422\u042b\u0415 \u0417\u0410\u041c\u0415\u041d\u042b \u0411\u0415\u0417 \u0426\u0418\u0424\u0420 ===\n            'yes': ('\u0414\u0430', r'\\\\bYes\\\\b'),\n            'no': ('\u041d\u0435\u0442', r'\\\\bNo\\\\b'),\n            'support': ('\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f', r'\\\\bSupport(?:ed)?\\\\b'),\n            'available': ('\u0414\u043e\u0441\u0442\u0443\u043f\u043d\u043e', r'\\\\bAvailable\\\\b'),\n            'enabled': ('\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e', r'\\\\bEnabled\\\\b'),\n            'disabled': ('\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e', r'\\\\bDisabled\\\\b'),\n            'optional': ('\u041e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e', r'\\\\bOptional\\\\b'),\n            'required': ('\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f', r'\\\\bRequired\\\\b'),\n            'manual': ('\u0420\u0443\u0447\u043d\u043e\u0439', r'\\\\bManual\\\\b'),\n            'automatic': ('\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439', r'\\\\bAutomatic\\\\b'),\n            'auto': ('\u0410\u0432\u0442\u043e', r'\\\\bAuto\\\\b'),\n            'high': ('\u0412\u044b\u0441\u043e\u043a\u0438\u0439', r'\\\\bHigh\\\\b'),\n            'medium': ('\u0421\u0440\u0435\u0434\u043d\u0438\u0439', r'\\\\bMedium\\\\b'),\n            'low': ('\u041d\u0438\u0437\u043a\u0438\u0439', r'\\\\bLow\\\\b'),\n            'none': ('\u041d\u0435\u0442', r'\\\\bNone\\\\b'),\n            'standard': ('\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439', r'\\\\bStandard\\\\b'),\n            'advanced': ('\u041f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439', r'\\\\bAdvanced\\\\b'),\n            'professional': ('\u041f\u0440\u043e\u0444\u0435\u0441\u0441\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439', r'\\\\bProfessional\\\\b'),\n            'indoor': ('\u0412\u043d\u0443\u0442\u0440\u0435\u043d\u043d\u0438\u0439', r'\\\\bIndoor\\\\b'),\n            'outdoor': ('\u041d\u0430\u0440\u0443\u0436\u043d\u044b\u0439', r'\\\\bOutdoor\\\\b'),\n            'wireless': ('\u0411\u0435\u0441\u043f\u0440\u043e\u0432\u043e\u0434\u043d\u043e\u0439', r'\\\\bWireless\\\\b'),\n            'wired': ('\u041f\u0440\u043e\u0432\u043e\u0434\u043d\u043e\u0439', r'\\\\bWired\\\\b'),\n            'ethernet': ('Ethernet', r'\\\\bEthernet\\\\b'),\n            'wifi': ('Wi-Fi', r'\\\\bWi-?Fi\\\\b'),\n            'bluetooth': ('Bluetooth', r'\\\\bBluetooth\\\\b'),\n            'poe': ('PoE', r'\\\\bPoE\\\\b'),\n            'poe+': ('PoE+', r'\\\\bPoE\\\\+\\\\b'),\n            'poe++': ('PoE++', r'\\\\bPoE\\\\+\\\\+\\\\b'),\n            'onvif': ('ONVIF', r'\\\\bONVIF\\\\b'),\n            'rtsp': ('RTSP', r'\\\\bRTSP\\\\b'),\n            'rtmp': ('RTMP', r'\\\\bRTMP\\\\b'),\n            'http': ('HTTP', r'\\\\bHTTP\\\\b'),\n            'https': ('HTTPS', r'\\\\bHTTPS\\\\b'),\n            'tcp': ('TCP', r'\\\\bTCP\\\\b'),\n            'udp': ('UDP', r'\\\\bUDP\\\\b'),\n            'ip': ('IP', r'\\\\bIP\\\\b'),\n            'ir': ('\u0418\u041a', r'\\\\bIR\\\\b'),\n            'led': ('\u0421\u0432\u0435\u0442\u043e\u0434\u0438\u043e\u0434', r'\\\\bLED\\\\b'),\n            'ccd': ('\u041f\u0417\u0421', r'\\\\bCCD\\\\b'),\n            'cmos': ('\u041a\u041c\u041e\u041f', r'\\\\bCMOS\\\\b'),\n            'ptz': ('\u041f\u0422\u0417', r'\\\\bPTZ\\\\b'),\n            'dvr': ('\u0412\u0438\u0434\u0435\u043e\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440', r'\\\\bDVR\\\\b'),\n            'nvr': ('\u0421\u0435\u0442\u0435\u0432\u043e\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440', r'\\\\bNVR\\\\b'),\n            'hdd': ('\u0416\u0435\u0441\u0442\u043a\u0438\u0439 \u0434\u0438\u0441\u043a', r'\\\\bHDD\\\\b'),\n            'ssd': ('SSD', r'\\\\bSSD\\\\b'),\n            'sd': ('SD \u043a\u0430\u0440\u0442\u0430', r'\\\\bSD\\\\b'),\n            'micro sd': ('MicroSD', r'\\\\bMicro\\\\s*SD\\\\b'),\n            'motion detection': ('\u0414\u0435\u0442\u0435\u043a\u0446\u0438\u044f \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f', r'\\\\bMotion\\\\s*Detection\\\\b'),\n            'night vision': ('\u041d\u043e\u0447\u043d\u043e\u0435 \u0432\u0438\u0434\u0435\u043d\u0438\u0435', r'\\\\bNight\\\\s*Vision\\\\b'),\n            'day/night': ('\u0414\u0435\u043d\u044c/\u041d\u043e\u0447\u044c', r'\\\\bDay/?Night\\\\b'),\n            'full hd': ('FullHD', r'\\\\bFull\\\\s*HD\\\\b'),\n            '4k': ('4K', r'\\\\b4K\\\\b'),\n            'ultra hd': ('UltraHD', r'\\\\bUltra\\\\s*HD\\\\b'),\n            'wide dynamic range': ('\u0428\u0438\u0440\u043e\u043a\u0438\u0439 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d', r'\\\\bWide\\\\s*Dynamic\\\\s*Range\\\\b'),\n            'wdr': ('\u0428\u0414\u0414', r'\\\\bWDR\\\\b'),\n            'true wdr': ('\u0418\u0441\u0442\u0438\u043d\u043d\u044b\u0439 \u0428\u0414\u0414', r'\\\\bTrue\\\\s*WDR\\\\b'),\n        }\n        \n        # \u0418\u0449\u0435\u043c \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445\n        found_rules = []\n        values_text = ' '.join(values).lower()\n        \n        for unit_key, (russian, pattern) in unit_patterns.items():\n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0432 \u0434\u0430\u043d\u043d\u044b\u0445\n            simple_pattern = unit_key.replace('_', ' ')\n            if any(simple_pattern in value.lower() for value in values):\n                # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0441 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u043e\u043c\n                rule_name = f\"\u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434: {unit_key.title()}\"\n                if unit_key in ['yes', 'no', 'support', 'available', 'enabled', 'disabled', \n                              'optional', 'required', 'manual', 'automatic', 'high', 'medium', \n                              'low', 'none', 'standard', 'advanced', 'professional', 'indoor', \n                              'outdoor', 'wireless', 'wired']:\n                    # \u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0437\u0430\u043c\u0435\u043d\u0430 \u0431\u0435\u0437 \u0433\u0440\u0443\u043f\u043f\n                    replacement = russian\n                else:\n                    # \u0417\u0430\u043c\u0435\u043d\u0430 \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0447\u0438\u0441\u043b\u0430\n                    replacement = f\"\\\\1 {russian}\"\n                \n                found_rules.append({\n                    'name': rule_name,\n                    'pattern': pattern,\n                    'replacement': replacement,\n                    'active': True\n                })\n        \n        return found_rules\n\n    def apply_replacement_rules(self, text):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u044b \u043a \u0442\u0435\u043a\u0441\u0442\u0443\"\"\"\n        if not hasattr(self, 'replacement_rules') or not self.replacement_rules:\n            return text\n        \n        original_text = text\n        result = text\n        applied_rules = []\n        \n        for rule in self.replacement_rules:\n            if rule.get('active', True):\n                try:\n                    pattern = rule.get('pattern', '')\n                    replacement = rule.get('replacement', '')\n                    old_result = result\n                    result = re.sub(pattern, replacement, result)\n                    if old_result != result:\n                        applied_rules.append(rule.get('name', 'Unnamed'))\n                except re.error:\n                    # \u0418\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u044f\n                    continue\n        \n        if applied_rules and original_text != result:\n            print(f\"\ud83d\udd04 \u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u044b: '{original_text}' \u2192 '{result}' (\u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u044b: {', '.join(applied_rules)})\")\n        \n        return result\n\n    def normalize_units_spacing(self, text):\n        \"\"\"\ud83c\udd95 \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u0432 \u043f\u0435\u0440\u0435\u0434 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f v4.9.2\"\"\"\n        if not text or not isinstance(text, str):\n            return text\n            \n        import re\n        \n        # \u0421\u043f\u0438\u0441\u043e\u043a \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\n        units = [\n            # \u0414\u043b\u0438\u043d\u0430\n            'mm', 'cm', 'm', 'km', 'in', 'ft',\n            # \u0412\u0435\u0441  \n            'mg', 'g', 'kg', 'lb', 'oz',\n            # \u042d\u043b\u0435\u043a\u0442\u0440\u0438\u0447\u0435\u0441\u0442\u0432\u043e\n            'V', 'A', 'W', 'Wh', 'kWh', 'mAh',\n            # \u0427\u0430\u0441\u0442\u043e\u0442\u0430\n            'Hz', 'kHz', 'MHz', 'GHz',\n            # \u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n            'bps', 'kbps', 'Mbps', 'Gbps', 'fps',\n            # \u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430\n            '\u00b0C', '\u00b0F', 'K',\n            # \u041e\u0441\u0432\u0435\u0449\u0435\u043d\u043d\u043e\u0441\u0442\u044c\n            'lux', 'lm', 'cd',\n            # \u041f\u0430\u043c\u044f\u0442\u044c\n            'B', 'KB', 'MB', 'GB', 'TB',\n            # \u0414\u0430\u0432\u043b\u0435\u043d\u0438\u0435\n            'Pa', 'kPa', 'MPa', 'bar', 'psi',\n            # \u0412\u0440\u0435\u043c\u044f\n            'ms', 's', 'min', 'h',\n        ]\n        \n        normalized = text\n        changes_made = 0\n        \n        # \u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0435\u0434\u0438\u043d\u0438\u0446\u044b \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0438\u0449\u0435\u043c \u043f\u0430\u0442\u0442\u0435\u0440\u043d \"\u0447\u0438\u0441\u043b\u043e+\u0435\u0434\u0438\u043d\u0438\u0446\u0430\" \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u043d\u0430 \"\u0447\u0438\u0441\u043b\u043e + \u0435\u0434\u0438\u043d\u0438\u0446\u0430\"\n        for unit in units:\n            # \u041f\u0430\u0442\u0442\u0435\u0440\u043d: \u0447\u0438\u0441\u043b\u043e (\u0446\u0435\u043b\u043e\u0435 \u0438\u043b\u0438 \u0434\u0440\u043e\u0431\u043d\u043e\u0435) + \u0435\u0434\u0438\u043d\u0438\u0446\u0430 \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u0435\u043b\u0430\n            pattern = r'(\\d+(?:\\.\\d+)?)\\s*' + re.escape(unit) + r'\\b'\n            replacement = r'\\1 ' + unit\n            \n            new_text = re.sub(pattern, replacement, normalized, flags=re.IGNORECASE)\n            if new_text != normalized:\n                changes_made += 1\n                print(f\"    \ud83d\udd27 \u041d\u041e\u0420\u041c\u0410\u041b\u0418\u0417\u0410\u0426\u0418\u042f: '{normalized}' \u2192 '{new_text}'\")\n                normalized = new_text\n        \n        if changes_made > 0:\n            print(f\"\ud83d\udd27 \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0435\u0434\u0438\u043d\u0438\u0446: {changes_made}\")\n            \n        return normalized\n\n    def apply_value_translation_rules(self, value_list):\n        \"\"\"\ud83c\udd95 \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u0410\u042f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 v4.9.2 - \u0441 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0435\u0434\u0438\u043d\u0438\u0446\"\"\"\n        try:\n            if not value_list:\n                return value_list\n            \n            print(f\"\ud83c\udf10 \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043a: {value_list}\")\n            \n            translated_values = []\n            translation_count = 0\n            protected_count = 0\n            normalization_count = 0\n            \n            for value in value_list:\n                translated_value = value\n                translation_applied = False\n                \n                # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u044b\n                if self.is_protected_abbreviation(value):\n                    print(f\"  \ud83d\udee1\ufe0f \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041e: '{value}' - \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440\u0430\")\n                    translated_values.append(value)\n                    protected_count += 1\n                    continue\n                \n                # \ud83c\udd95 \u0421\u041d\u0410\u0427\u0410\u041b\u0410 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043f\u0440\u043e\u0431\u0435\u043b\u044b \u043f\u0435\u0440\u0435\u0434 \u0435\u0434\u0438\u043d\u0438\u0446\u0430\u043c\u0438 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n                normalized_value = self.normalize_units_spacing(translated_value)\n                # \ud83c\udd95 \u0414\u041e\u041f\u041e\u041b\u041d\u0418\u0422\u0415\u041b\u042c\u041d\u041e \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u043e\u0440\u043c\u0430\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n                normalized_value = normalize_value_format(normalized_value)\n                if normalized_value != translated_value:\n                    translated_value = normalized_value\n                    normalization_count += 1\n                    print(f\"    \ud83d\udd27 \u041d\u041e\u0420\u041c\u0410\u041b\u0418\u0417\u041e\u0412\u0410\u041d\u041e: '{value}' \u2192 '{normalized_value}'\")\n                \n                # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0438\u0437 translation_dict\n                if hasattr(self, 'translation_system') and self.translation_system:\n                    try:\n                        basic_translation = self.translation_system.translation_dict.get(translated_value)\n                        if basic_translation and basic_translation != translated_value:\n                            translated_value = basic_translation\n                            translation_count += 1\n                            translation_applied = True\n                            print(f\"    \u2705 \u0411\u0410\u0417\u041e\u0412\u041e\u0415 \u041f\u0420\u0410\u0412\u0418\u041b\u041e: '{normalized_value}' \u2192 '{basic_translation}'\")\n                    except Exception as e:\n                        print(f\"    \u26a0\ufe0f \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u043f\u0440\u0430\u0432\u0438\u043b: {e}\")\n                \n                # \u0415\u0441\u043b\u0438 \u0431\u0430\u0437\u043e\u0432\u043e\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\n                if not translation_applied and hasattr(self, 'value_translation_rules') and self.value_translation_rules:\n                    for rule in self.value_translation_rules:\n                        if rule.get(\"active\", True):\n                            original_pattern = rule.get(\"original\", \"\")\n                            translation = rule.get(\"translation\", \"\")\n                            \n                            if original_pattern and translation:\n                                # \u0422\u043e\u0447\u043d\u043e\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 (\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u043e-\u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0435)\n                                if translated_value.lower() == original_pattern.lower():\n                                    translated_value = translation\n                                    translation_count += 1\n                                    translation_applied = True\n                                    print(f\"    \u2705 \u041f\u041e\u041b\u042c\u0417\u041e\u0412\u0410\u0422\u0415\u041b\u042c\u0421\u041a\u041e\u0415 \u041f\u0420\u0410\u0412\u0418\u041b\u041e (\u0442\u043e\u0447\u043d\u043e\u0435): '{original_pattern}' \u2192 '{translation}'\")\n                                    break\n                                # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 (\u0434\u043b\u044f \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f)\n                                elif original_pattern.lower() in translated_value.lower():\n                                    import re\n                                    pattern = r'\\b' + re.escape(original_pattern) + r'\\b'\n                                    if re.search(pattern, translated_value, re.IGNORECASE):\n                                        new_value = re.sub(pattern, translation, translated_value, flags=re.IGNORECASE)\n                                        if new_value != translated_value:\n                                            translated_value = new_value\n                                            translation_count += 1\n                                            translation_applied = True\n                                            print(f\"    \u2705 \u041f\u041e\u041b\u042c\u0417\u041e\u0412\u0410\u0422\u0415\u041b\u042c\u0421\u041a\u041e\u0415 \u041f\u0420\u0410\u0412\u0418\u041b\u041e (\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u0435): '{value}' \u2192 '{translated_value}'\")\n                                            break\n                \n                # \u0415\u0441\u043b\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c, \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0434\u043b\u044f \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f\n                if not translation_applied:\n                    # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430 kbps/Mbps \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0439\n                    import re\n                    speed_patterns = {\n                        r'\\b(\\d+(?:\\.\\d+)?)\\s+kbps\\b': r'\\1 \u043a\u0431\u0438\u0442/\u0441',\n                        r'\\b(\\d+(?:\\.\\d+)?)\\s+Mbps\\b': r'\\1 \u041c\u0431\u0438\u0442/\u0441',\n                        r'\\b(\\d+(?:\\.\\d+)?)\\s+Gbps\\b': r'\\1 \u0413\u0431\u0438\u0442/\u0441',\n                        r'\\b(\\d+(?:\\.\\d+)?)\\s+bps\\b': r'\\1 \u0431\u0438\u0442/\u0441',\n                        r'\\b(\\d+(?:\\.\\d+)?)\\s+fps\\b': r'\\1 \u043a/\u0441',\n                    }\n                    \n                    for pattern, replacement in speed_patterns.items():\n                        new_value = re.sub(pattern, replacement, translated_value, flags=re.IGNORECASE)\n                        if new_value != translated_value:\n                            translated_value = new_value\n                            translation_count += 1\n                            translation_applied = True\n                            print(f\"    \u2705 \u041f\u0410\u0422\u0422\u0415\u0420\u041d \u0421\u041a\u041e\u0420\u041e\u0421\u0422\u0418: '{value}' \u2192 '{translated_value}'\")\n                            break\n                \n                translated_values.append(translated_value)\n            \n            if normalization_count > 0:\n                print(f\"\ud83d\udd27 \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {normalization_count}\")\n            if translation_count > 0:\n                print(f\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {translation_count}\")\n            if protected_count > 0:\n                print(f\"\ud83d\udee1\ufe0f \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043e \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440: {protected_count}\")\n            \n            return translated_values\n        \n        except Exception as e:\n            print(f\"\u274c \u041e\u0428\u0418\u0411\u041a\u0410 \u0432 apply_value_translation_rules: {e}\")\n            return value_list\n\n    def log_print(self, message, log_file=None):\n        \"\"\"\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438 \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c, \u0438 \u0432 \u043b\u043e\u0433-\u0444\u0430\u0439\u043b\"\"\"\n        print(message)\n        if log_file:\n            try:\n                with open(log_file, 'a', encoding='utf-8') as f:\n                    f.write(message + '\\n')\n            except Exception as e:\n                print(f\"\u26a0\ufe0f \u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u043b\u043e\u0433: {e}\")\n\n    def save_results(self):\n        if self.original_df is None:\n            messagebox.showwarning(\"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u0435 \u0444\u0430\u0439\u043b!\")\n            return\n\n        # \ud83c\udd95 \u0421\u041e\u0417\u0414\u0410\u041d\u0418\u0415 \u0424\u0410\u0419\u041b\u0410 \u041b\u041e\u0413\u041e\u0412\n        from datetime import datetime\n        timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n        log_filename = f\"debug_export_{timestamp}.log\"\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043b\u043e\u0433-\u0444\u0430\u0439\u043b \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\n        try:\n            with open(log_filename, 'w', encoding='utf-8') as f:\n                f.write(f\"\u041e\u0422\u041b\u0410\u0414\u041e\u0427\u041d\u042b\u0419 \u041b\u041e\u0413 \u042d\u041a\u0421\u041f\u041e\u0420\u0422\u0410\\n\")\n                f.write(f\"\u0412\u0440\u0435\u043c\u044f: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\")\n                f.write(f\"=\" * 80 + \"\\n\\n\")\n        except Exception as e:\n            print(f\"\u26a0\ufe0f \u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043b\u043e\u0433-\u0444\u0430\u0439\u043b: {e}\")\n            log_filename = None\n\n        # \ud83c\udd95 \u041e\u0422\u041b\u0410\u0414\u041e\u0427\u041d\u0410\u042f \u0418\u041d\u0424\u041e\u0420\u041c\u0410\u0426\u0418\u042f \u0414\u041b\u042f \u0414\u0418\u0410\u0413\u041d\u041e\u0421\u0422\u0418\u041a\u0418 \u041f\u0420\u041e\u0411\u041b\u0415\u041c\u042b\n        self.log_print(\"\\n\" + \"=\"*80, log_filename)\n        self.log_print(\"\ud83d\udd0d \u041e\u0422\u041b\u0410\u0414\u041a\u0410 SAVE_RESULTS \u041d\u0410\u0427\u0410\u0422\u0410\", log_filename)\n        self.log_print(\"=\"*80, log_filename)\n        self.log_print(f\"\ud83d\udcca \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0444\u0430\u0439\u043b: {self.original_df.shape[0]} \u0441\u0442\u0440\u043e\u043a, {self.original_df.shape[1]} \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\", log_filename)\n        self.log_print(f\"\ud83d\udccb \u0421\u0442\u043e\u043b\u0431\u0446\u044b \u0444\u0430\u0439\u043b\u0430: {list(self.original_df.columns)}\", log_filename)\n\n        # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0438\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\n        extra_col_names = self.settings.get(\"extra_cols\", [])\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0447\u0442\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0432 DataFrame\n        available_extra_cols = [col for col in extra_col_names if col in self.original_df.columns]\n        \n        if extra_col_names and not available_extra_cols:\n            self.log_print(f\"\u26a0\ufe0f \u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435: \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u044b {extra_col_names} \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u0432 \u0444\u0430\u0439\u043b\u0435\", log_filename)\n        \n        cols_to_save = [\"\u0410\u0440\u0442\u0438\u043a\u0443\u043b\"] + available_extra_cols\n        self.log_print(f\"\ud83d\udccb \u0421\u0442\u043e\u043b\u0431\u0446\u044b \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f: {cols_to_save}\", log_filename)\n\n        result = []\n\n        html_col_idx = int(self.features_col.get()) - 1\n        self.log_print(f\"\ud83d\udccb \u0418\u043d\u0434\u0435\u043a\u0441 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a: {html_col_idx} (\u0441\u0442\u043e\u043b\u0431\u0435\u0446: {self.original_df.columns[html_col_idx]})\", log_filename)\n        \n        # \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u043d\u044b\u0435 \u0442\u043e\u0432\u0430\u0440\u044b \u0434\u043b\u044f \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f\n        problematic_articles = ['IPC523-F255-NR', 'IPC2241-FN/FNW-SIR', 'IPC427', 'IPC422', 'IPC980']\n        processed_problematic = []\n\n        for idx, (_, row) in enumerate(self.original_df.iterrows()):\n            sku = row.get(\"\u0410\u0440\u0442\u0438\u043a\u0443\u043b\", None)\n            if sku is None:\n                continue\n                \n            is_problematic = sku in problematic_articles\n            if is_problematic:\n                self.log_print(f\"\\n\ud83d\udd0d \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0410 \u041f\u0420\u041e\u0411\u041b\u0415\u041c\u041d\u041e\u0413\u041e \u0422\u041e\u0412\u0410\u0420\u0410 {idx}: {sku}\", log_filename)\n                \n            item_data = {col: row[col] for col in cols_to_save if col in row}\n\n            html = str(row[self.original_df.columns[html_col_idx]])\n            \n            if is_problematic:\n                self.log_print(f\"   \ud83d\udccb HTML \u0434\u043b\u0438\u043d\u0430: {len(html)}\", log_filename)\n                self.log_print(f\"   \ud83d\udccb HTML \u043d\u0430\u0447\u0430\u043b\u043e: {html[:100]}...\", log_filename)\n                \n            params = self.parse_html_table(html)\n            \n            if is_problematic:\n                self.log_print(f\"   \u2705 \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043d\u0430\u0439\u0434\u0435\u043d\u043e: {len(params)}\", log_filename)\n                if len(params) == 0:\n                    self.log_print(f\"   \u274c \u041d\u0415 \u041d\u0410\u0419\u0414\u0415\u041d\u041e \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412 \u0434\u043b\u044f {sku}!\", log_filename)\n                else:\n                    self.log_print(f\"   \ud83d\udccb \u041f\u0435\u0440\u0432\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b: {list(params.keys())[:3]}...\", log_filename)\n\n            param_count = 0\n            for param, value in params.items():\n                try:\n                    clean_value = self.remove_duplicates_from_cell(value)\n                    mapped_param = self.mappings.get(param, param)\n\n                    delimiter = \";\"\n                    node_id = None\n                    for iid in self.tree.get_children():\n                        vals = self.tree.item(iid, \"values\")\n                        if vals[1] == param:\n                            node_id = iid\n                            break\n                    if node_id:\n                        try:\n                            delimiter_val = self.tree.item(node_id, \"values\")[4].strip()\n                            if delimiter_val:\n                                delimiter = delimiter_val\n                        except:\n                            pass\n                    \n                    # \ud83c\udd95 \u0424\u0418\u041d\u0410\u041b\u042c\u041d\u0410\u042f \u0417\u0410\u0429\u0418\u0422\u0410 \u041e\u0422 \u041f\u0423\u0421\u0422\u041e\u0413\u041e \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n                    if not delimiter:\n                        delimiter = \";\"\n                        self.log_print(f\"      \u26a0\ufe0f \u041f\u0423\u0421\u0422\u041e\u0419 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u0437\u0430\u043c\u0435\u043d\u0435\u043d \u043d\u0430 ';'\", log_filename)\n\n                    split_vals = self.split_values(clean_value, delimiter)\n                    \n                    if is_problematic and param_count == 0:  # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n                        self.log_print(f\"   \ud83d\udd04 \u041f\u0435\u0440\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 '{param}':\", log_filename)\n                        self.log_print(f\"      \u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435: {value[:50]}...\", log_filename)\n                        self.log_print(f\"      \u041e\u0447\u0438\u0449\u0435\u043d\u043d\u043e\u0435: {clean_value[:50]}...\", log_filename)\n                        self.log_print(f\"      \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c: '{delimiter}'\", log_filename)\n                        self.log_print(f\"      \u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435: {split_vals}\", log_filename)\n                        \n                    param_count += 1\n                except Exception as e:\n                    if is_problematic:\n                        self.log_print(f\"   \u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 {param}: {e}\", log_filename)\n                    continue\n\n                # \ud83c\udd95 \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u0410\u042f \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0410: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043c\u0430\u043f\u043f\u0438\u043d\u0433 \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041d\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\n                split_vals_mapped = [val.strip() for val in split_vals if val.strip()]\n                \n                self.log_print(f\"\ud83d\udd04 \u041c\u041d\u041e\u0416\u0415\u0421\u0422\u0412\u0415\u041d\u041d\u042b\u0415 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u0418 '{delimiter}': {len(split_vals)} \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u2192 {split_vals_mapped}\", log_filename)\n\n                split_vals_mapped = [self.apply_case_normalization(v) for v in split_vals_mapped]\n                \n                # \ud83c\udd95 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u0432 split_vals_mapped\n                split_vals_mapped = [self.restore_protected_values_in_string(v) for v in split_vals_mapped]\n                \n                seen_normalized = set()\n                final_split_vals = []\n                for v in split_vals_mapped:\n                    key_norm = normalize_variant(v).lower()\n                    if key_norm not in seen_normalized:\n                        seen_normalized.add(key_norm)\n                        final_split_vals.append(v)\n\n                if param in self.manual_split_results:\n                    result_str = self.manual_split_results[param]\n                    # \ud83c\udd95 \u041f\u0420\u0415\u041e\u0411\u0420\u0410\u0417\u041e\u0412\u0410\u041d\u0418\u0415 \u0434\u043b\u044f WooCommerce: \";\" \u2192 \", \" (\u0437\u0430\u043f\u044f\u0442\u0430\u044f \u0441 \u043f\u0440\u043e\u0431\u0435\u043b\u043e\u043c)\n                    if \"; \" in result_str:\n                        result_str = result_str.replace(\"; \", \", \")\n                        self.log_print(f\"\ud83d\udd04 \u041f\u0420\u0415\u041e\u0411\u0420\u0410\u0417\u041e\u0412\u0410\u041d\u041e \u0434\u043b\u044f WooCommerce: '{param}' \u2192 '; ' \u0437\u0430\u043c\u0435\u043d\u0435\u043d\u043e \u043d\u0430 ', '\", log_filename)\n                else:\n                    result_str = \", \".join(final_split_vals)\n\n                    # \ud83c\udd95 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u043f\u0440\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435\n                    result_str = self.restore_protected_values_in_string(result_str)\n\n                    item_data[mapped_param] = result_str\n                    \n                    if is_problematic and param_count == 1:  # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0434\u043b\u044f \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n                        self.log_print(f\"      \u0424\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{result_str[:50]}...'\", log_filename)\n\n            if is_problematic:\n                self.log_print(f\"   \ud83d\udcca \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {param_count}\", log_filename)\n                self.log_print(f\"   \ud83d\udcca \u0418\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u0435\u0439 \u0432 \u0442\u043e\u0432\u0430\u0440\u0435: {len(item_data)}\", log_filename)\n                if len(item_data) <= 1:  # \u0422\u043e\u043b\u044c\u043a\u043e \u0410\u0440\u0442\u0438\u043a\u0443\u043b\n                    self.log_print(f\"   \u274c \u0422\u041e\u0412\u0410\u0420 \u0411\u0423\u0414\u0415\u0422 \u041f\u0423\u0421\u0422\u042b\u041c!\", log_filename)\n                    processed_problematic.append(f\"{sku} - \u041f\u0423\u0421\u0422\u041e\u0419\")\n                else:\n                    self.log_print(f\"   \u2705 \u0422\u043e\u0432\u0430\u0440 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d \u0443\u0441\u043f\u0435\u0448\u043d\u043e\", log_filename)\n                    processed_problematic.append(f\"{sku} - \u041e\u041a ({len(item_data)-1} \u043f\u043e\u043b\u0435\u0439)\")\n\n            result.append(item_data)\n\n        df_result = pd.DataFrame(result)\n        \n        self.log_print(f\"\\n\ud83d\udcca \u0418\u0422\u041e\u0413\u041e\u0412\u0410\u042f \u0421\u0422\u0410\u0422\u0418\u0421\u0422\u0418\u041a\u0410:\", log_filename)\n        self.log_print(f\"   \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043e \u0442\u043e\u0432\u0430\u0440\u043e\u0432: {len(result)}\", log_filename)\n        self.log_print(f\"   \u0421\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435: {len(df_result.columns) if not df_result.empty else 0}\", log_filename)\n        self.log_print(f\"   \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u043d\u044b\u0435 \u0442\u043e\u0432\u0430\u0440\u044b:\", log_filename)\n        for status in processed_problematic:\n            self.log_print(f\"      {status}\", log_filename)\n        self.log_print(\"=\"*80, log_filename)\n        self.log_print(\"\ud83d\udd0d \u041e\u0422\u041b\u0410\u0414\u041a\u0410 SAVE_RESULTS \u0417\u0410\u0412\u0415\u0420\u0428\u0415\u041d\u0410\", log_filename)\n        self.log_print(\"=\"*80, log_filename)\n        \n        # \u0421\u043e\u043e\u0431\u0449\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u043b\u043e\u0433\u0430\n        if log_filename:\n            self.log_print(f\"\\n\ud83d\udcbe \u041e\u0422\u041b\u0410\u0414\u041e\u0427\u041d\u042b\u0419 \u041b\u041e\u0413 \u0421\u041e\u0425\u0420\u0410\u041d\u0415\u041d \u0412 \u0424\u0410\u0419\u041b: {log_filename}\", log_filename)\n            messagebox.showinfo(\"\u041e\u0442\u043b\u0430\u0434\u043a\u0430\", f\"\u041e\u0442\u043b\u0430\u0434\u043e\u0447\u043d\u044b\u0439 \u043b\u043e\u0433 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d \u0432 \u0444\u0430\u0439\u043b:\\n{log_filename}\\n\\n\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b \u0434\u043b\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u043e\u043c.\")\n        \n        # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\n        output_folder = self.output_folder_var.get().strip()\n        if output_folder and os.path.exists(output_folder):\n            initial_dir = output_folder\n        else:\n            initial_dir = os.getcwd()\n        \n        # \u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c \u0438\u043c\u044f \u0444\u0430\u0439\u043b\u0430 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e\n        source_file = self.source_file_var.get().strip()\n        if source_file:\n            base_name = os.path.splitext(os.path.basename(source_file))[0]\n            suggested_name = f\"{base_name}_\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0439.xlsx\"\n        else:\n            suggested_name = \"\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442_\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.xlsx\"\n        \n        suggested_path = os.path.join(initial_dir, suggested_name)\n        \n        save_path = filedialog.asksaveasfilename(\n            title=\"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\",\n            initialdir=initial_dir,\n            initialfile=suggested_name,\n            defaultextension=\".xlsx\", \n            filetypes=[(\"Excel \u0444\u0430\u0439\u043b\u044b\", \"*.xlsx\"), (\"\u0412\u0441\u0435 \u0444\u0430\u0439\u043b\u044b\", \"*.*\")]\n        )\n\n        if save_path:\n            try:\n                df_result.to_excel(save_path, index=False)\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", f\"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d \u0432 {save_path}\")\n                \n                # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0430\u043f\u043a\u0443 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445\n                saved_folder = os.path.dirname(save_path)\n                self.output_folder_var.set(saved_folder)\n                self.save_settings()\n                \n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\", str(e))\n\n    def on_close(self):\n        \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\"\"\"\n        self.save_settings()\n        self.save_merge_settings()\n        self.root.destroy()\n\n    def open_replacement_rules_editor(self):\n        dialog = ReplacementRulesEditorDialog(self.root, self.replacement_rules, self.save_replacement_rules)\n\n    def open_case_normalization_dialog(self):\n        dialog = CaseNormalizationDialog(self.root, self.case_normalization, self.save_case_normalization)\n    \n    # \ud83c\udd95 \u041d\u041e\u0412\u042b\u0415 \u041c\u0415\u0422\u041e\u0414\u042b v4.0 - \u0421\u0438\u043d\u043e\u043d\u0438\u043c\u044b \u0438 \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\n    def open_value_synonyms_dialog(self):\n        \"\"\"\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        dialog = ValueSynonymsDialog(self.root, self.synonyms_manager)\n        if dialog.result:\n            # \u041f\u043e\u0441\u043b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\n            self.refresh_treeview_with_colors()\n            messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", \"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b\")\n    \n    def open_protected_values_dialog(self):\n        \"\"\"\ud83c\udd95 \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438\"\"\"\n        dialog = Toplevel(self.root)\n        dialog.title(\"\ud83d\udee1\ufe0f \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\")\n        dialog.geometry(\"600x500\")\n        dialog.transient(self.root)\n        dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        dialog.update_idletasks()\n        x = (dialog.winfo_screenwidth() // 2) - (dialog.winfo_width() // 2)\n        y = (dialog.winfo_screenheight() // 2) - (dialog.winfo_height() // 2)\n        dialog.geometry(f\"+{x}+{y}\")\n        \n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c\n        header_frame = Frame(dialog)\n        header_frame.pack(fill=X, padx=10, pady=10)\n        \n        Label(header_frame, text=\"\ud83d\udee1\ufe0f \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", \n              font=(\"Arial\", 14, \"bold\")).pack()\n        \n        info_text = (\"\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u041d\u0415 \u0411\u0423\u0414\u0423\u0422 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0441\u0438\u043c\u0432\u043e\u043b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f.\\n\"\n                    \"\u041f\u0440\u0438\u043c\u0435\u0440: \u043f\u0440\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435 '/' \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 'B/W' \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043a\u0430\u043a 'B/W', \u0430 \u043d\u0435 \u0441\u0442\u0430\u043d\u0435\u0442 'B; W'\")\n        Label(header_frame, text=info_text, \n              font=(\"Arial\", 9), fg=\"gray\", wraplength=550, justify=LEFT).pack(pady=5)\n        \n        # \u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n        example_frame = LabelFrame(dialog, text=\"\ud83d\udca1 \u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\", padx=5, pady=5)\n        example_frame.pack(fill=X, padx=10, pady=5)\n        \n        example_text = ('\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435: \"Auto (ICR) / Color / B/W\"\\n'\n                       '\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c: \"/\" (\u0431\u0435\u0437 \u0437\u0430\u0449\u0438\u0442\u044b) \u2192 \"Auto (ICR); Color; B; W\" \u274c\\n'\n                       '\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c: \"/\" (\u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \"B/W\") \u2192 \"Auto (ICR); Color; B/W\" \u2705')\n        Label(example_frame, text=example_text, font=(\"Courier\", 9), \n              justify=LEFT, bg=\"#FFFACD\").pack(fill=X)\n        \n        # \u0421\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        list_frame = LabelFrame(dialog, text=\"\ud83d\udccb \u0422\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", padx=5, pady=5)\n        list_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c Treeview \u0434\u043b\u044f \u0441\u043f\u0438\u0441\u043a\u0430\n        tree_frame = Frame(list_frame)\n        tree_frame.pack(fill=BOTH, expand=True)\n        \n        protected_tree = ttk.Treeview(tree_frame, columns=('value',), show='headings', height=10)\n        protected_tree.heading('value', text='\u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435')\n        protected_tree.column('value', width=500)\n        protected_tree.pack(side=LEFT, fill=BOTH, expand=True)\n        \n        scrollbar = Scrollbar(tree_frame, orient=VERTICAL, command=protected_tree.yview)\n        scrollbar.pack(side=RIGHT, fill=Y)\n        protected_tree.configure(yscrollcommand=scrollbar.set)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0442\u0435\u043a\u0443\u0449\u0438\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438\n        def refresh_protected_list():\n            for item in protected_tree.get_children():\n                protected_tree.delete(item)\n            for value in self.protected_values:\n                protected_tree.insert('', END, values=(value,))\n        \n        refresh_protected_list()\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        buttons_frame = Frame(list_frame)\n        buttons_frame.pack(fill=X, pady=5)\n        \n        def add_protected_value():\n            \"\"\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u043e\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\"\"\"\n            value = simpledialog.askstring(\n                \"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\", \n                \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u041d\u0415 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f:\\n(\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: B/W, I/O, AC/DC, USB-C)\"\n            )\n            if value and value.strip():\n                clean_value = value.strip()\n                if clean_value not in self.protected_values:\n                    self.protected_values.append(clean_value)\n                    self.save_protected_values()\n                    refresh_protected_list()\n                    messagebox.showinfo(\"\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e\", f\"\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 '{clean_value}' \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435\")\n                else:\n                    messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", f\"\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 '{clean_value}' \u0443\u0436\u0435 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435\")\n        \n        def remove_protected_value():\n            \"\"\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\"\"\"\n            selection = protected_tree.selection()\n            if not selection:\n                messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\")\n                return\n            \n            for item in selection:\n                value = protected_tree.item(item, 'values')[0]\n                if value in self.protected_values:\n                    self.protected_values.remove(value)\n            \n            self.save_protected_values()\n            refresh_protected_list()\n            messagebox.showinfo(\"\u0423\u0434\u0430\u043b\u0435\u043d\u043e\", \"\u0412\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u044b\")\n        \n        def add_common_values():\n            \"\"\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\"\"\"\n            common_values = [\"B/W\", \"I/O\", \"AC/DC\", \"USB-C\", \"Wi-Fi\", \"Bluetooth\", \"RJ-45\", \"RS-232\", \"RS-485\"]\n            added = []\n            \n            for value in common_values:\n                if value not in self.protected_values:\n                    self.protected_values.append(value)\n                    added.append(value)\n            \n            if added:\n                self.save_protected_values()\n                refresh_protected_list()\n                messagebox.showinfo(\"\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e\", f\"\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e {len(added)} \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439:\\n\" + \", \".join(added))\n            else:\n                messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u0412\u0441\u0435 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0443\u0436\u0435 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435\")\n        \n        Button(buttons_frame, text=\"\u2795 \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\", command=add_protected_value).pack(side=LEFT, padx=2)\n        Button(buttons_frame, text=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=remove_protected_value).pack(side=LEFT, padx=2)\n        Button(buttons_frame, text=\"\ud83d\udce5 \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0435\", command=add_common_values).pack(side=LEFT, padx=2)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0430 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f\n        close_frame = Frame(dialog)\n        close_frame.pack(fill=X, padx=10, pady=10)\n        \n        Button(close_frame, text=\"\u2705 \u0413\u043e\u0442\u043e\u0432\u043e\", command=dialog.destroy, bg=\"#E6FFE6\").pack()\n    \n    def detect_similar_parameters(self):\n        \"\"\"\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\"\"\n        if not hasattr(self, 'tree') or not self.tree.get_children():\n            return\n        \n        # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n        all_params = []\n        for item in self.tree.get_children():\n            values = self.tree.item(item, \"values\")\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            if param not in all_params:\n                all_params.append(param)\n        \n        # \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b\n        self.similar_param_groups = {}\n        processed = set()\n        color_index = 0\n        \n        for param1 in all_params:\n            if param1 in processed:\n                continue\n                \n            similar_group = [param1]\n            processed.add(param1)\n            \n            # \u0418\u0449\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n            for param2 in all_params:\n                if param2 == param1 or param2 in processed:\n                    continue\n                    \n                # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0435\u0441\u0442\u044c\n                similarity = fuzz.ratio(param1.lower(), param2.lower())\n                if similarity >= 70:  # \u041f\u043e\u0440\u043e\u0433 \u043f\u043e\u0445\u043e\u0436\u0435\u0441\u0442\u0438\n                    similar_group.append(param2)\n                    processed.add(param2)\n            \n            # \u0415\u0441\u043b\u0438 \u0433\u0440\u0443\u043f\u043f\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0431\u043e\u043b\u0435\u0435 \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430, \u043d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u0446\u0432\u0435\u0442\n            if len(similar_group) > 1:\n                representative = min(similar_group, key=len)  # \u0421\u0430\u043c\u044b\u0439 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044c\n                self.similar_param_groups[representative] = similar_group\n                \n                # \u041d\u0430\u0437\u043d\u0430\u0447\u0430\u0435\u043c \u0446\u0432\u0435\u0442 \u0432\u0441\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c \u0433\u0440\u0443\u043f\u043f\u044b\n                color = self.similar_param_colors[color_index % len(self.similar_param_colors)]\n                for param in similar_group:\n                    self.param_color_map[param] = color\n                color_index += 1\n        \n        print(f\"\ud83c\udfa8 \u041d\u0430\u0439\u0434\u0435\u043d\u043e \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {len(self.similar_param_groups)}\")\n        for rep, group in self.similar_param_groups.items():\n            print(f\"  \ud83c\udfa8 {rep}: {group}\")\n    \n    def refresh_treeview_with_colors(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0434\u0435\u0440\u0435\u0432\u0430 \u0441 \u0446\u0432\u0435\u0442\u043e\u0432\u044b\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c\"\"\"\n        if not hasattr(self, 'tree'):\n            return\n            \n        # \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n        self.detect_similar_parameters()\n        \n        # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0446\u0432\u0435\u0442\u0430\n        for item in self.tree.get_children():\n            values = self.tree.item(item, \"values\")\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            \n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0446\u0432\u0435\u0442 \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n            if param in self.param_color_map:\n                color = self.param_color_map[param]\n                # \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0442\u0435\u0433\u0438 \u0434\u043b\u044f \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0433\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n                tag_name = f\"color_{color.replace('#', '')}\"\n                self.tree.set(item, tags=(tag_name,))\n                self.tree.tag_configure(tag_name, background=color)\n            else:\n                # \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u0446\u0432\u0435\u0442\u043e\u0432\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\n                self.tree.set(item, tags=())\n\n    def load_mappings(self):\n        if os.path.exists(self.MAPPINGS_FILE):\n            try:\n                with open(self.MAPPINGS_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.mappings = json.load(f)\n            except:\n                self.mappings = {}\n        else:\n            self.mappings = {}\n\n    def save_mappings(self):\n        try:\n            with open(self.MAPPINGS_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.mappings, f, ensure_ascii=False, indent=2)\n        except Exception as e:\n            print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432: {e}\")\n\n    def refresh_mapping_table(self):\n        if not hasattr(self, 'mapping_tree'):\n            return\n            \n        self.mapping_tree.delete(*self.mapping_tree.get_children())\n        self._mapping_items_cache = []\n        for orig, repl in self.mappings.items():\n            self._mapping_items_cache.append((orig, repl))\n        self._mapping_items_cache.sort(key=lambda x: x[0].lower())\n        for orig, repl in self._mapping_items_cache:\n            self.mapping_tree.insert(\"\", \"end\", values=(orig, repl))\n\n    def update_main_tree_with_mappings(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 \u0443\u0447\u0435\u0442\u043e\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u0418 \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\"\"\"\n        updated_count = 0\n        \n        for item in self.tree.get_children():\n            values = list(self.tree.item(item, \"values\"))\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            orig_values = values[2]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n            delimiter = values[4]\n            if not delimiter:\n                delimiter = \";\"\n\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f + \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434\n            # \u0428\u0430\u0433 1: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0437 \u0442\u0430\u0431\u043b\u0438\u0446\u044b\n            mapped_param = self.mappings.get(param, param)\n            \n            # \u0428\u0430\u0433 2: \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043a \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0443 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n            new_param = self.apply_auto_translation(mapped_param, is_parameter=True)\n            \n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f \u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            if values[3] != new_param:\n                updated_count += 1\n                \n            values[3] = new_param\n\n            if param in self.manual_split_results:\n                split_result_str = self.manual_split_results[param]\n            else:\n                if param in self.manual_split_values and isinstance(self.manual_split_values[param], str) and self.manual_split_values[param] != False:\n                    split_vals = [v.strip() for v in self.manual_split_values[param].split(\";\") if v.strip()]\n                else:\n                    # \ud83c\udd95 \u0414\u041e\u041f\u041e\u041b\u041d\u0418\u0422\u0415\u041b\u042c\u041d\u0410\u042f \u0417\u0410\u0429\u0418\u0422\u0410 \u041e\u0422 \u041f\u0423\u0421\u0422\u041e\u0413\u041e \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n                    if not delimiter:\n                        delimiter = \";\"\n                        print(f\"  \u26a0\ufe0f \u0414\u0423\u0411\u041b\u0418\u0420\u041e\u0412\u0410\u041d\u041d\u0410\u042f \u0417\u0410\u0429\u0418\u0422\u0410: \u043f\u0443\u0441\u0442\u043e\u0439 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u2192 ';' \u0434\u043b\u044f '{param}'\")\n                    \n                    split_vals = self.split_values(orig_values, delimiter)\n\n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0411\u0415\u0417 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c (\u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432!)\n                split_vals_processed = [val.strip() for val in split_vals if val.strip()]\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 1.\u041f\u043e\u0441\u043b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f: {split_vals_processed}\")\n                \n                split_vals_processed = [self.apply_case_normalization(v) for v in split_vals_processed]\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 2.\u041f\u043e\u0441\u043b\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438: {split_vals_processed}\")\n                \n                split_vals_processed = self.remove_duplicates_from_list(split_vals_processed)\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 3.\u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0439 \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438: {split_vals_processed}\")\n                \n                # \ud83c\udd95 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\n                split_vals_processed = [self.restore_protected_values_in_string(v) for v in split_vals_processed]\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 4.\u041f\u043e\u0441\u043b\u0435 \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f: {split_vals_processed}\")\n                \n                # \ud83c\udd95 \u041f\u0420\u0418\u041c\u0415\u041d\u042f\u0415\u041c \u041f\u0420\u0410\u0412\u0418\u041b\u0410 \u0417\u0410\u041c\u0415\u041d\u042b\n                split_vals_processed = [self.apply_replacement_rules(v) for v in split_vals_processed]\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 5.\u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u0430\u0432\u0438\u043b \u0437\u0430\u043c\u0435\u043d\u044b: {split_vals_processed}\")\n                \n                # \ud83c\udd95 \u041f\u0420\u0418\u041c\u0415\u041d\u042f\u0415\u041c \u0421\u0418\u041d\u041e\u041d\u0418\u041c\u042b \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419\n                split_vals_processed = self.synonyms_manager.merge_synonyms_in_list(split_vals_processed)\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 6.\u041f\u043e\u0441\u043b\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {split_vals_processed}\")\n                \n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b \u041f\u041e\u0421\u041b\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n                split_vals_processed = self.remove_duplicates_from_list(split_vals_processed)\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 7.\u041f\u043e\u0441\u043b\u0435 \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432: {split_vals_processed}\")\n                \n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041d\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c (\u0442\u043e\u043b\u044c\u043a\u043e \u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c!)\n                # \u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432\u044b\u0437\u044b\u0432\u0430\u043b \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0442\u0438\u043f\u0430 \"Audio in\" \u2192 \"\u0410\u0443\u0434\u0438\u043e in\"\n                # \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 \u0437\u0430\u043c\u0435\u043d\u044b \u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\u043c\u0438\n                \n                # \ud83d\udd27 \u0424\u0418\u041d\u0410\u041b\u042c\u041d\u0410\u042f \u0414\u0415\u0414\u0423\u041f\u041b\u0418\u041a\u0410\u0426\u0418\u042f (\u0431\u0435\u0437 \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430)\n                split_vals_processed = self.remove_duplicates_from_list(split_vals_processed)\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 8.\u0424\u0418\u041d\u0410\u041b\u042c\u041d\u0410\u042f \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f: {split_vals_processed}\")\n                \n                split_result_str = \"; \".join(split_vals_processed)\n                print(f\"\ud83d\udd27 DEBUG UPDATE {param}: 9.\u0418\u0422\u041e\u0413\u041e\u0412\u042b\u0419 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{split_result_str}'\")\n\n            values[5] = split_result_str\n            self.tree.item(item, values=values)\n        \n        # \u0412\u044b\u0432\u043e\u0434\u0438\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\u0445 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0431\u044b\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\n        if updated_count > 0:\n            print(f\"\ud83d\udd04 \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435: {updated_count}\")\n\n    def load_excl(self):\n        if os.path.exists(self.EXCLUSIONS_FILE):\n            try:\n                with open(self.EXCLUSIONS_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.exclusions = json.load(f)\n            except:\n                self.exclusions = []\n        else:\n            self.exclusions = []\n\n    def load_protected_values(self):\n        \"\"\"\ud83c\udd95 \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\"\"\n        if os.path.exists(self.PROTECTED_VALUES_FILE):\n            try:\n                with open(self.PROTECTED_VALUES_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.protected_values = json.load(f)\n                print(f\"\ud83d\udee1\ufe0f \u0417\u0410\u0413\u0420\u0423\u0416\u0415\u041d\u041e {len(self.protected_values)} \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {self.protected_values}\")\n            except Exception as e:\n                print(f\"\u26a0\ufe0f \u041e\u0448\u0438\u0431\u043a\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {e}\")\n                self.protected_values = []\n        else:\n            self.protected_values = []\n\n    def save_protected_values(self):\n        \"\"\"\ud83c\udd95 \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        try:\n            with open(self.PROTECTED_VALUES_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.protected_values, f, ensure_ascii=False, indent=2)\n            print(f\"\ud83d\udcbe \u0421\u041e\u0425\u0420\u0410\u041d\u0415\u041d\u041e {len(self.protected_values)} \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\")\n        except Exception as e:\n            print(f\"\u26a0\ufe0f \u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {e}\")\n\n    def restore_protected_values_in_string(self, text):\n        \"\"\"\ud83c\udd95 \u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438\u0437 \u043f\u043b\u0435\u0439\u0441\u0445\u043e\u043b\u0434\u0435\u0440\u043e\u0432 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435\"\"\"\n        if not text or not self.protected_values:\n            return text\n            \n        # \u0418\u0449\u0435\u043c \u0438 \u0437\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u043b\u0435\u0439\u0441\u0445\u043e\u043b\u0434\u0435\u0440\u044b \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n        restored_text = text\n        for i, protected_value in enumerate(self.protected_values):\n            placeholder = f\"__PROTECTED_{i}__\"\n            if placeholder in restored_text:\n                restored_text = restored_text.replace(placeholder, protected_value)\n                print(f\"\ud83d\udd13 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u041e \u0432 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435: '{placeholder}' \u2192 '{protected_value}'\")\n        \n        return restored_text\n\n    def load_case_normalization(self):\n        if os.path.exists(self.CASE_NORM_FILE):\n            try:\n                with open(self.CASE_NORM_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.case_normalization = json.load(f)\n            except:\n                self.case_normalization = {}\n        else:\n            self.case_normalization = {}\n\n    def load_replacement_rules(self):\n        if os.path.exists(self.REPLACEMENT_RULES_FILE):\n            try:\n                with open(self.REPLACEMENT_RULES_FILE, \"r\", encoding=\"utf-8\") as f:\n                    self.replacement_rules = json.load(f)\n            except:\n                self.replacement_rules = []\n        else:\n            self.replacement_rules = []\n\n    def save_replacement_rules(self, rules):\n        self.replacement_rules = rules\n        try:\n            with open(self.REPLACEMENT_RULES_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.replacement_rules, f, ensure_ascii=False, indent=2)\n            \n            # \ud83c\udd95 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b \u0437\u0430\u043c\u0435\u043d\u044b v4.9\n            print(\"\ud83d\udd04 \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b \u0437\u0430\u043c\u0435\u043d\u044b...\")\n            self.dynamic_update_main_table()\n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\u044b: {str(e)}\")\n\n    def save_case_normalization(self, case_norm):\n        self.case_normalization = case_norm\n        try:\n            with open(self.CASE_NORM_FILE, \"w\", encoding=\"utf-8\") as f:\n                json.dump(self.case_normalization, f, ensure_ascii=False, indent=2)\n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438: {str(e)}\")\n\n    def update_exclusions_tree(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u0435\u0440\u0435\u0432\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439\"\"\"\n        for item in self.exclusions_tree.get_children():\n            self.exclusions_tree.delete(item)\n        \n        for exclusion in self.exclusions:\n            self.exclusions_tree.insert('', END, values=(exclusion,))\n\n    def add_exclusion(self):\n        \"\"\"\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\"\"\"\n        exclusion = simpledialog.askstring(\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\", \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435:\")\n        if exclusion and exclusion.strip():\n            self.exclusions.append(exclusion.strip())\n            self.update_exclusions_tree()\n\n    def delete_exclusion(self):\n        \"\"\"\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\"\"\"\n        selection = self.exclusions_tree.selection()\n        if selection:\n            for item in selection:\n                exclusion = self.exclusions_tree.item(item, 'values')[0]\n                if exclusion in self.exclusions:\n                    self.exclusions.remove(exclusion)\n            self.update_exclusions_tree()\n\n    def filter_mapping_table(self, *args):\n        if not hasattr(self, '_mapping_items_cache'):\n            return\n            \n        search_text = self.search_var.get().lower()\n        self.mapping_tree.delete(*self.mapping_tree.get_children())\n        filtered = []\n        for orig, repl in self._mapping_items_cache:\n            if search_text in orig.lower():\n                filtered.append((orig, repl))\n        for orig, repl in filtered:\n            self.mapping_tree.insert(\"\", \"end\", values=(orig, repl))\n\n    def sort_mapping_tree_by_original_col(self):\n        if not hasattr(self, '_mapping_items_cache'):\n            return\n            \n        self.mapping_sort_reverse = not self.mapping_sort_reverse\n        self._mapping_items_cache.sort(key=lambda x: x[0].lower(), reverse=self.mapping_sort_reverse)\n        self.mapping_tree.delete(*self.mapping_tree.get_children())\n        for orig, repl in self._mapping_items_cache:\n            self.mapping_tree.insert(\"\", \"end\", values=(orig, repl))\n\n    def add_mapping(self):\n        original = simpledialog.askstring(\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435\", \"\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435:\")\n        if original:\n            replacement = simpledialog.askstring(\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435\", \"\u0417\u0430\u043c\u0435\u043d\u0430:\")\n            if replacement is not None:\n                self.mappings[original] = replacement\n                self.save_mappings()\n                self.refresh_mapping_table()\n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n                self.update_main_tree_with_mappings()\n\n    def delete_mapping(self):\n        selected = self.mapping_tree.selection()\n        if selected:\n            for item in selected:\n                values = self.mapping_tree.item(item, 'values')\n                original = values[0]\n                if original in self.mappings:\n                    del self.mappings[original]\n            self.save_mappings()\n            self.refresh_mapping_table()\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n            self.update_main_tree_with_mappings()\n\n    def delete_all_mappings(self):\n        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f?\"):\n            self.mappings.clear()\n            self.save_mappings()\n            self.refresh_mapping_table()\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n            self.update_main_tree_with_mappings()\n\n    def import_mappings(self):\n        filename = filedialog.askopenfilename(filetypes=[(\"JSON files\", \"*.json\")])\n        if filename:\n            try:\n                with open(filename, \"r\", encoding=\"utf-8\") as f:\n                    imported_mappings = json.load(f)\n                self.mappings.update(imported_mappings)\n                self.save_mappings()\n                self.refresh_mapping_table()\n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n                self.update_main_tree_with_mappings()\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e {len(imported_mappings)} \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439\")\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c: {str(e)}\")\n\n    def export_mappings(self):\n        filename = filedialog.asksaveasfilename(\n            defaultextension=\".json\",\n            filetypes=[(\"JSON files\", \"*.json\")]\n        )\n        if filename:\n            try:\n                with open(filename, \"w\", encoding=\"utf-8\") as f:\n                    json.dump(self.mappings, f, ensure_ascii=False, indent=2)\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043e {len(self.mappings)} \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439\")\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c: {str(e)}\")\n\n    def update_df_from_treeview(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 DataFrame \u0438\u0437 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e Treeview\"\"\"\n        if not hasattr(self, 'tree') or not self.tree.get_children():\n            return\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 DataFrame \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e treeview\n        self.df = pd.DataFrame([{\n            \"\u2116\": int(values[0]),\n            \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\": values[1],\n            \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\": values[2],\n            \"\u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\": values[3],\n            \"\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c\": values[4],\n            \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\": values[5]\n        } for item in self.tree.get_children() for values in [self.tree.item(item, \"values\")]])\n\n    def normalize_all_values_format(self, show_message=True):\n        \"\"\"\u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u043e\u0440\u043c\u0430\u0442\u0430 \u0432\u0441\u0435\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        \n        Args:\n            show_message (bool): \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043b\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0438 (False \u043f\u0440\u0438 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0435)\n        \"\"\"\n        if not hasattr(self, 'df') or self.df is None or self.df.empty:\n            if show_message:\n                messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0443\")\n            return\n        \n        count = 0\n        for item in self.tree.get_children():\n            values = list(self.tree.item(item, \"values\"))\n            split_result = values[5]\n            if split_result:\n                normalized_values = []\n                for val in split_result.split(\";\"):\n                    val = val.strip()\n                    if val:\n                        normalized_val = normalize_value_format(val)\n                        normalized_values.append(normalized_val)\n                # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u043f\u043e\u0440\u044f\u0434\u043e\u043a\n                seen = set()\n                unique_normalized = []\n                for v in normalized_values:\n                    v_lower = v.lower()\n                    if v_lower not in seen:\n                        seen.add(v_lower)\n                        unique_normalized.append(v)\n                new_split_result = \"; \".join(unique_normalized)\n                if new_split_result != split_result:\n                    values[5] = new_split_result\n                    self.tree.item(item, values=values)\n                    count += 1\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c df \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435\n        self.update_df_from_treeview()\n        \n        if show_message:\n            messagebox.showinfo(\"\u0417\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e\", f\"\u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0444\u043e\u0440\u043c\u0430\u0442 \u0432 {count} \u0441\u0442\u0440\u043e\u043a\u0430\u0445\")\n\n    def force_refresh_delimiters(self):\n        \"\"\"\ud83d\udd27 \u041c\u0410\u0421\u0421\u041e\u0412\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0432\u0441\u0435\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 (\u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e - \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438)\"\"\"\n        if not hasattr(self, 'df') or self.df is None or self.df.empty:\n            messagebox.showwarning(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0443\")\n            return\n        \n        # \u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0430\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\n        result = messagebox.askyesno(\n            \"\u041c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\", \n            \"\ud83d\udd04 \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438!\\n\\n\" +\n            \"\u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0443\u0436\u043d\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0441\u0435\u0445 \u0441\u0442\u0440\u043e\u043a.\\n\\n\" +\n            \"\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435?\"\n        )\n        \n        if not result:\n            return\n        \n        print(\"\\n\ud83d\udd04 \u041c\u0410\u0421\u0421\u041e\u0412\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0412\u0421\u0415\u0425 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u0415\u0419 \u041d\u0410\u0427\u0410\u0422\u041e\")\n        print(\"=\" * 80)\n        \n        updated_count = 0\n        \n        for item in self.tree.get_children():\n            values = list(self.tree.item(item, \"values\"))\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            orig_values = values[2]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n            delimiter = values[4]  # \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c\n            current_result = values[5]  # \u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n            \n            # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n            print(f\"\\n\ud83d\udd0d \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415: '{param}'\")\n            print(f\"  \ud83d\udccb \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c: '{delimiter}'\")\n            print(f\"  \ud83d\udccb \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: '{orig_values}'\")\n            print(f\"  \ud83d\udccb \u0422\u0435\u043a\u0443\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{current_result}'\")\n            \n            # \ud83c\udd95 \u0417\u0410\u0429\u0418\u0422\u0410 \u041e\u0422 \u041f\u0423\u0421\u0422\u041e\u0413\u041e \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n            if not delimiter:\n                delimiter = \";\"\n                print(f\"  \u26a0\ufe0f \u0417\u0410\u041c\u0415\u041d\u0410 \u043f\u0443\u0441\u0442\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f \u2192 ';'\")\n            \n            # \ud83c\udd95 \u0418\u0421\u041f\u041e\u041b\u042c\u0417\u0423\u0415\u041c \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u0423\u042e \u0424\u0423\u041d\u041a\u0426\u0418\u042e split_values\n            new_split_vals = self.split_values(orig_values, delimiter)\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041d\u0415 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\n            split_vals_mapped = [val.strip() for val in new_split_vals if val.strip()]\n            \n            # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n            split_vals_mapped = [self.apply_case_normalization(v) for v in split_vals_mapped]\n            \n            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b\n            split_vals_mapped = self.remove_duplicates_from_list(split_vals_mapped)\n            \n            # \ud83c\udd95 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u0432 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435\n            split_vals_mapped = [self.restore_protected_values_in_string(v) for v in split_vals_mapped]\n            \n            # \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\n            new_result = \"; \".join(split_vals_mapped)\n            \n            print(f\"  \u2705 \u041d\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{new_result}'\")\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0441\u044f\n            if new_result != current_result:\n                values[5] = new_result\n                self.tree.item(item, values=values)\n                updated_count += 1\n                print(f\"  \ud83d\udd04 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u041e!\")\n            else:\n                print(f\"  \u2713 \u0411\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439\")\n        \n        print(f\"\\n\u2705 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0412\u0415\u0420\u0428\u0415\u041d\u041e: {updated_count} \u0441\u0442\u0440\u043e\u043a \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043e\")\n        \n        if updated_count > 0:\n            messagebox.showinfo(\"\u041c\u0430\u0441\u0441\u043e\u0432\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u043e\", \n                               f\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e {updated_count} \u0441\u0442\u0440\u043e\u043a.\\n\\n\" +\n                               \"\ud83d\udca1 \u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438 \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438!\")\n        else:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \n                               \"\u0412\u0441\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438 \u0443\u0436\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b.\\n\\n\" +\n                               \"\ud83d\udca1 \u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438!\")\n\n    # \u041e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b - \u0437\u0430\u0433\u043b\u0443\u0448\u043a\u0438 \u0434\u043b\u044f \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438  \n    def on_double_click(self, event):\n        region = self.tree.identify(\"region\", event.x, event.y)\n        if region != \"cell\":\n            return\n\n        column = self.tree.identify_column(event.x)\n        item = self.tree.identify_row(event.y)\n\n        if not item or not column:\n            return\n\n        col_index = int(column.replace('#', '')) - 1\n        values = list(self.tree.item(item, \"values\"))\n        orig_param = values[1]\n        current_value = values[col_index]\n\n        if col_index not in [3, 4, 5]:\n            return\n\n        if self.edit_entry:\n            self.edit_entry.destroy()\n            self.edit_entry = None\n            self.edit_undo_stack.clear()\n\n        entry = Entry(self.tree)\n        entry.insert(0, current_value)\n        entry.select_range(0, END)\n        entry.focus_set()\n\n        x, y, width, height = self.tree.bbox(item, column)\n        entry.place(x=x, y=y, width=width, height=height)\n\n        self.edit_undo_stack = [current_value]\n        self.edit_entry = entry\n\n        def save_and_destroy(event=None):\n            new_value = entry.get().strip()\n            values[col_index] = new_value\n\n            if col_index == 4:  # \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c\n                self.manual_split_values[orig_param] = new_value\n                self.settings[\"manual_split_values\"] = self.manual_split_values.copy()\n                self.save_settings()\n                \n                # \ud83c\udd95 \u041f\u0420\u041e\u0421\u0422\u041e\u0415 \u0418 \u041d\u0410\u0414\u0415\u0416\u041d\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f v4.8.1\n                print(f\"\\n\ud83d\udd04 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f \u0434\u043b\u044f '{orig_param}'\")\n                print(f\"  \ud83d\udccb \u041d\u043e\u0432\u044b\u0439 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c: '{new_value}'\")\n                \n                # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0441\u0447\u0435\u0442\u0430\n                orig_values = values[2]  # \u0421\u0442\u043e\u043b\u0431\u0435\u0446 \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\"\n                print(f\"  \ud83d\udccb \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f: '{orig_values}'\")\n                \n                # \u041f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n                if not orig_values:\n                    new_result = \"\"\n                else:\n                    # \u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u043e \u043d\u043e\u0432\u043e\u043c\u0443 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044e \u0438\u043b\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u043c\u0443\n                    delimiter = new_value if new_value else \";\"\n                    split_parts = [part.strip() for part in orig_values.split(delimiter) if part.strip()]\n                    print(f\"  \ud83d\udccb \u0420\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438: {split_parts}\")\n                    \n                    # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443\n                    processed_parts = []\n                    for part in split_parts:\n                        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u0435 \u0438 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n                        clean_part = part.strip()\n                        if clean_part and clean_part not in [\"/\", \";\", \",\", \"-\"]:\n                            processed_parts.append(clean_part)\n                    \n                    print(f\"  \ud83d\udccb \u041e\u0447\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438: {processed_parts}\")\n                    \n                    # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u043f\u043e\u0440\u044f\u0434\u043e\u043a\n                    unique_parts = []\n                    seen = set()\n                    for part in processed_parts:\n                        if part.lower() not in seen:\n                            seen.add(part.lower())\n                            unique_parts.append(part)\n                    \n                    print(f\"  \ud83d\udccb \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438: {unique_parts}\")\n                    \n                    # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0422\u041e\u041b\u042c\u041a\u041e \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0435\u0441\u0442\u044c\n                    if hasattr(self, 'value_translation_rules') and self.value_translation_rules:\n                        try:\n                            translated_parts = self.apply_value_translation_rules(unique_parts)\n                            unique_parts = translated_parts\n                            print(f\"  \ud83d\udccb \u041f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {unique_parts}\")\n                        except Exception as e:\n                            print(f\"  \u26a0\ufe0f \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {e}\")\n                    \n                    # \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\n                    new_result = \"; \".join(unique_parts)\n                \n                print(f\"  \u2705 \u0418\u0422\u041e\u0413\u041e\u0412\u042b\u0419 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{new_result}'\")\n                \n                # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\" (\u0438\u043d\u0434\u0435\u043a\u0441 5)\n                values[5] = new_result\n            elif col_index == 3:  # \u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n                old_map_val = self.mappings.get(orig_param, orig_param)\n                if old_map_val != new_value and new_value.strip():\n                    self.mappings[orig_param] = new_value.strip()\n                    self.save_mappings()\n                    self.refresh_mapping_table()\n                    self.update_main_tree_with_mappings()\n\n            self.tree.item(item, values=values)\n\n            entry.destroy()\n            self.edit_entry = None\n            self.edit_undo_stack.clear()\n\n        entry.bind(\"<Return>\", save_and_destroy)\n        entry.bind(\"<FocusOut>\", save_and_destroy)\n\n    def show_context_menu(self, event):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e\"\"\"\n        try:\n            self.context_menu.post(event.x_root, event.y_root)\n        except:\n            pass\n\n    def sort_column(self, event):\n        \"\"\"\u0421\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u043a\u043e\u043b\u043e\u043d\u043a\u0438\"\"\"\n        # \u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f - \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u043f\u0440\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438\n        pass\n\n    def copy_selected_value(self, event=None):\n        \"\"\"\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\"\"\"\n        try:\n            selection = self.tree.selection()\n            if selection:\n                item = selection[0]\n                values = self.tree.item(item, \"values\")\n                if values:\n                    pyperclip.copy(str(values[1]))  # \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n        except:\n            pass\n\n    def select_all_rows(self, event):\n        \"\"\"\u0412\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0441\u0442\u0440\u043e\u043a\"\"\"\n        try:\n            for item in self.tree.get_children():\n                self.tree.selection_add(item)\n        except:\n            pass\n\n    def on_mapping_double_click(self, event):\n        \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u0432\u043e\u0439\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u043a\u0430 \u043d\u0430 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0435 - \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0442\u043e\u043b\u0431\u0446\u0430 '\u0417\u0430\u043c\u0435\u043d\u0430'\"\"\"\n        print(f\"\ud83d\udd0d DEBUG: \u0414\u0432\u043e\u0439\u043d\u043e\u0439 \u043a\u043b\u0438\u043a \u043d\u0430 mapping_tree! x={event.x}, y={event.y}\")\n        \n        try:\n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\n            selection = self.mapping_tree.selection()\n            print(f\"\ud83d\udd0d DEBUG: selection = {selection}\")\n            \n            if not selection:\n                print(\"\ud83d\udd0d DEBUG: \u041d\u0435\u0442 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\")\n                return\n                \n            item = selection[0]\n            values = self.mapping_tree.item(item, \"values\")\n            print(f\"\ud83d\udd0d DEBUG: values = {values}\")\n            \n            if len(values) < 2:\n                print(f\"\ud83d\udd0d DEBUG: \u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {len(values)}\")\n                return\n                \n            original_value = values[0]  # \u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n            current_replacement = values[1]  # \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0437\u0430\u043c\u0435\u043d\u0430\n            \n            print(f\"\ud83d\udd0d DEBUG: \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f '{original_value}' \u2192 '{current_replacement}'\")\n            \n            # \ud83d\udd27 \u0423\u041f\u0420\u041e\u0429\u0415\u041d\u041e: \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0440\u0438 \u043b\u044e\u0431\u043e\u043c \u0434\u0432\u043e\u0439\u043d\u043e\u043c \u043a\u043b\u0438\u043a\u0435\n            new_replacement = simpledialog.askstring(\n                \"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043c\u0435\u043d\u0443\", \n                f\"\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: {original_value}\\n\\n\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u043e\u0432\u0443\u044e \u0437\u0430\u043c\u0435\u043d\u0443:\",\n                initialvalue=current_replacement\n            )\n            \n            if new_replacement is not None and new_replacement != current_replacement:\n                # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043c\u0430\u043f\u043f\u0438\u043d\u0433\n                self.mappings[original_value] = new_replacement\n                self.save_mappings()\n                self.refresh_mapping_table()\n                \n                # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n                self.update_main_tree_with_mappings()\n                \n                print(f\"\ud83d\udcdd \u0418\u0437\u043c\u0435\u043d\u0435\u043d\u043e \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435: '{original_value}' \u2192 '{new_replacement}'\")\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", f\"\u0421\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e:\\n'{original_value}' \u2192 '{new_replacement}'\")\n            elif new_replacement is None:\n                print(\"\ud83d\udd0d DEBUG: \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u0442\u043c\u0435\u043d\u0438\u043b \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\")\n            else:\n                print(\"\ud83d\udd0d DEBUG: \u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c\")\n                \n        except Exception as e:\n            print(f\"\ud83d\udd0d DEBUG: \u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430: {e}\")\n            import traceback\n            traceback.print_exc()\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043e\u0442\u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435:\\n{e}\")\n\n    def select_all_mappings(self, event=None):\n        \"\"\"\u0412\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432\"\"\"\n        try:\n            for item in self.mapping_tree.get_children():\n                self.mapping_tree.selection_add(item)\n        except:\n            pass\n\n    def delete_selected_mappings(self, event):\n        \"\"\"\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432\"\"\"\n        try:\n            selection = self.mapping_tree.selection()\n            for item in selection:\n                values = self.mapping_tree.item(item, \"values\")\n                if len(values) >= 2:\n                    orig_param = values[0]\n                    if orig_param in self.mappings:\n                        del self.mappings[orig_param]\n                self.mapping_tree.delete(item)\n        except:\n            pass\n\n    def copy_mapping_selected_value(self, event):\n        \"\"\"\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430\"\"\"\n        try:\n            selection = self.mapping_tree.selection()\n            if selection:\n                item = selection[0]\n                values = self.mapping_tree.item(item, \"values\")\n                if values:\n                    pyperclip.copy(str(values[0]))\n        except:\n            pass\n\n    def on_exclusion_double_click(self, event):\n        \"\"\"\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u0432\u043e\u0439\u043d\u043e\u0433\u043e \u043a\u043b\u0438\u043a\u0430 \u043d\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438\"\"\"\n        # \u041c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\n        pass\n\n    def copy_exclusion_selected_value(self, event):\n        \"\"\"\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\"\"\"\n        try:\n            selection = self.exclusions_tree.selection()\n            if selection:\n                item = selection[0]\n                values = self.exclusions_tree.item(item, \"values\")\n                if values:\n                    pyperclip.copy(str(values[0]))\n        except:\n            pass\n\n    def global_ctrl_c(self, event):\n        \"\"\"\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 (Ctrl+C)\"\"\"\n        try:\n            widget = self.root.focus_get()\n            if hasattr(widget, 'selection_get'):\n                pyperclip.copy(widget.selection_get())\n        except:\n            pass\n\n    def global_ctrl_v(self, event):\n        \"\"\"\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u0432\u0441\u0442\u0430\u0432\u043a\u0430 (Ctrl+V)\"\"\"\n        try:\n            widget = self.root.focus_get()\n            if hasattr(widget, 'insert'):\n                widget.insert('insert', pyperclip.paste())\n        except:\n            pass\n\n    def global_ctrl_z(self, event):\n        \"\"\"\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0442\u043c\u0435\u043d\u0430 (Ctrl+Z)\"\"\"\n        # \u041c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u044b\n        pass\n\n    def global_ctrl_a(self, event):\n        \"\"\"\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0433\u043e (Ctrl+A)\"\"\"\n        try:\n            widget = self.root.focus_get()\n            if hasattr(widget, 'select_range'):\n                widget.select_range(0, 'end')\n            elif widget == self.tree:\n                self.select_all_rows(event)\n        except:\n            pass\n\n    def global_delete(self, event):\n        \"\"\"\u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 (Delete)\"\"\"\n        try:\n            widget = self.root.focus_get()\n            if widget == self.tree:\n                # \u041c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u0440\u043e\u043a\n                pass\n            elif widget == self.mapping_tree:\n                self.delete_selected_mappings(event)\n        except:\n            pass\n\n    def merge_selected_similar_params(self):\n        \"\"\"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        selection = self.tree.selection()\n        if len(selection) < 2:\n            messagebox.showwarning(\"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\")\n            return\n        \n        # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043c\u0435\u043d\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        selected_params = []\n        for item in selection:\n            values = self.tree.item(item, \"values\")\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            selected_params.append(param)\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n        dialog = ParameterMergeDialog(self.root, selected_params)\n        if dialog.result:\n            representative = dialog.result\n            merge_groups = {representative: selected_params}\n            self.apply_parameter_merges(merge_groups)\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\n            print(f\"\ud83d\udd04 \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u0435\u043c: '{representative}'\")\n            \n            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0435\u0441\u043b\u0438 \u0435\u0433\u043e \u043d\u0435\u0442\n            translation_copied = False\n            if representative not in self.mappings:\n                # \u0418\u0449\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0441\u0440\u0435\u0434\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n                for param in selected_params:\n                    if param in self.mappings:\n                        # \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043d\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f\n                        self.mappings[representative] = self.mappings[param]\n                        print(f\"\ud83d\udccb \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d \u043f\u0435\u0440\u0435\u0432\u043e\u0434: '{representative}' \u2190 '{param}' \u2192 '{self.mappings[param]}'\")\n                        translation_copied = True\n                        break\n            \n            # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0438\n            if translation_copied:\n                self.save_mappings()\n                self.refresh_mapping_table()\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\n            self.update_main_tree_with_mappings()\n            \n            messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", \n                f\"\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u044b \u043f\u043e\u0434 \u0438\u043c\u0435\u043d\u0435\u043c '{representative}'\\n\"\n                f\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\")\n    \n    def merge_selected_params_and_values(self):\n        \"\"\"\u042d\u041a\u0421\u0422\u0420\u0410-\u041e\u0422\u041b\u0410\u0414\u041e\u0427\u041d\u0410\u042f \u0432\u0435\u0440\u0441\u0438\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        print(\"\\n\" + \"\ud83d\udd25\"*100)\n        print(\"\ud83d\udd25 \u042d\u041a\u0421\u0422\u0420\u0410-\u041e\u0422\u041b\u0410\u0414\u041a\u0410: merge_selected_params_and_values \u041d\u0410\u0427\u0410\u041b\u041e\")\n        print(\"\ud83d\udd25\"*100)\n        \n        selection = self.tree.selection()\n        print(f\"\ud83d\udccb \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432: {len(selection)}\")\n        \n        if len(selection) < 2:\n            messagebox.showwarning(\"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\")\n            return\n        \n        # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445\n        selected_items_info = []\n        selected_params = []\n        \n        print(\"\\n\ud83d\udd25 \u0410\u041d\u0410\u041b\u0418\u0417 \u0412\u042b\u0411\u0420\u0410\u041d\u041d\u042b\u0425 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412:\")\n        for i, item in enumerate(selection):\n            values = self.tree.item(item, \"values\")\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            selected_params.append(param)\n            selected_items_info.append({\n                'item': item,\n                'param': param,\n                'original_values': values[2],\n                'result_values': values[5]\n            })\n            print(f\"  \ud83d\udccb \u042d\u043b\u0435\u043c\u0435\u043d\u0442 {i+1}: '{param}' \u2192 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435: '{values[2]}' \u2192 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: '{values[5]}'\")\n        \n        print(f\"\\n\ud83d\udd25 \u0421\u041f\u0418\u0421\u041e\u041a \u0412\u042b\u0411\u0420\u0410\u041d\u041d\u042b\u0425 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412: {selected_params}\")\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n        dialog = ParameterMergeDialog(self.root, selected_params)\n        if dialog.result:\n            representative = dialog.result\n            print(f\"\\n\ud83d\udd25 \u0412\u042b\u0411\u0420\u0410\u041d\u041d\u042b\u0419 \u041e\u0421\u041d\u041e\u0412\u041d\u041e\u0419 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420: '{representative}'\")\n            \n            # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0433\u0440\u0443\u043f\u043f\u0443 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\n            merge_groups = {representative: selected_params}\n            print(f\"\ud83d\udd25 \u0421\u0424\u041e\u0420\u041c\u0418\u0420\u041e\u0412\u0410\u041d\u041d\u042b\u0415 \u0413\u0420\u0423\u041f\u041f\u042b: {merge_groups}\")\n            \n            # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n            self.apply_parameter_merges(merge_groups)\n            \n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u041d\u0423\u0416\u041d\u041e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b!\n            # \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043a \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044e (representative)\n            print(f\"\ud83d\udd04 \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u0435\u043c: '{representative}'\")\n            \n            # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0435\u0441\u043b\u0438 \u0435\u0433\u043e \u043d\u0435\u0442\n            translation_copied_extended = False\n            if representative not in self.mappings:\n                # \u0418\u0449\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0441\u0440\u0435\u0434\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n                for param in selected_params:\n                    if param in self.mappings:\n                        # \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043d\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f\n                        self.mappings[representative] = self.mappings[param]\n                        print(f\"\ud83d\udccb \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d \u043f\u0435\u0440\u0435\u0432\u043e\u0434: '{representative}' \u2190 '{param}' \u2192 '{self.mappings[param]}'\")\n                        translation_copied_extended = True\n                        break\n            \n            # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0438\n            if translation_copied_extended:\n                self.save_mappings()\n                self.refresh_mapping_table()\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\n            self.update_main_tree_with_mappings()\n            \n            messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0448\u043d\u043e\", \n                f\"\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u044b \u043f\u043e\u0434 \u0438\u043c\u0435\u043d\u0435\u043c '{representative}'\\n\"\n                f\"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u043e {len(selected_params)} \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\\n\"\n                f\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\")\n            \n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f\n            if representative not in self.param_merge_settings:\n                self.param_merge_settings[representative] = {\n                    'similar_params': selected_params,\n                    'selected': True\n                }\n            \n            self.save_merge_settings()\n            \n            print(\"\\n\ud83d\udd25 \u042d\u041a\u0421\u0422\u0420\u0410-\u041e\u0422\u041b\u0410\u0414\u041a\u0410: merge_selected_params_and_values \u0417\u0410\u0412\u0415\u0420\u0428\u0415\u041d\u041e\")\n            print(\"\ud83d\udd25\"*100)\n\n    def apply_parameter_merges(self, merge_groups):\n        \"\"\"\n        \u0418\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432:\n        - \u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u0432\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u0432\u0441\u0435\u0445 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        - \u0412 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 '\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f' \u043f\u0438\u0448\u0435\u0442 \u0432\u0441\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u0434\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430), \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435\n        - \u0412 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 '\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f' \u043f\u0438\u0448\u0435\u0442 \u0432\u0441\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435\n        - \u0414\u0443\u0431\u043b\u0438 \u0443\u0434\u0430\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u0439\n        \"\"\"\n        print(\"\\n\" + \"\ud83d\udd25\"*80)\n        print(\"\ud83d\udd25 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041d\u0410\u042f \u0412\u0415\u0420\u0421\u0418\u042f \u041e\u0411\u042a\u0415\u0414\u0418\u041d\u0415\u041d\u0418\u042f - \u041f\u041e\u041b\u041d\u041e\u0415 \u0420\u0415\u0428\u0415\u041d\u0418\u0415 \u041f\u0420\u041e\u0411\u041b\u0415\u041c\")\n        print(\"\ud83d\udd25\"*80)\n        \n        if not merge_groups:\n            print(\"\u274c \u041d\u0435\u0442 \u0433\u0440\u0443\u043f\u043f \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\")\n            return\n        \n        print(f\"\ud83d\udccb \u0413\u0440\u0443\u043f\u043f\u044b \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f: {merge_groups}\")\n        \n        for representative, param_list in merge_groups.items():\n            print(f\"\\n\ud83d\udd25 \u0413\u0420\u0423\u041f\u041f\u0410: '{representative}' \u2190 {param_list}\")\n            rows_to_merge = []\n            target_row = None\n            for item in self.tree.get_children():\n                values = self.tree.item(item, \"values\")\n                param_name = values[1]\n                if param_name in param_list:\n                    rows_to_merge.append({\n                        'item': item,\n                        'param': param_name,\n                        'original': values[2],\n                        'result': values[5],\n                        'delimiter': values[4] or \";\"\n                    })\n                    if param_name == representative:\n                        target_row = item\n            if len(rows_to_merge) < 2:\n                print(f\"  \u26a0\ufe0f \u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0442\u0440\u043e\u043a \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f: {len(rows_to_merge)}\")\n                continue\n            # \u0421\u0431\u043e\u0440 \u0432\u0441\u0435\u0445 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 (\u0434\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430)\n            all_original_values = []\n            for row in rows_to_merge:\n                source_text = row['result'] if row['result'] else row['original']\n                if source_text:\n                    parts = self._smart_split_values(source_text, row['delimiter'])\n                    for part in parts:\n                        clean_part = normalize_value_format(part.strip())\n                        if clean_part:\n                            all_original_values.append(clean_part)\n            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438 (\u0431\u0435\u0437 \u0443\u0447\u0451\u0442\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430)\n            seen = set()\n            unique_original_values = []\n            for v in all_original_values:\n                v_lower = v.lower()\n                if v_lower not in seen:\n                    seen.add(v_lower)\n                    unique_original_values.append(v)\n            # \u0421\u0431\u043e\u0440 \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n            translated_values = []\n            for v in unique_original_values:\n                # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043a \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e\n                translated = self.apply_value_translation_rules([v])[0] if hasattr(self, 'apply_value_translation_rules') else v\n                translated = normalize_value_format(translated.strip())\n                if translated:\n                    translated_values.append(translated)\n            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n            seen_tr = set()\n            unique_translated_values = []\n            for v in translated_values:\n                v_lower = v.lower()\n                if v_lower not in seen_tr:\n                    seen_tr.add(v_lower)\n                    unique_translated_values.append(v)\n            # \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438\n            final_delimiter = rows_to_merge[0]['delimiter'] if rows_to_merge else \";\"\n            original_result = final_delimiter.join(unique_original_values)\n            translated_result = final_delimiter.join(unique_translated_values)\n            # \u0412\u044b\u0431\u043e\u0440 \u0446\u0435\u043b\u0435\u0432\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438\n            if not target_row:\n                target_row = rows_to_merge[0]['item']\n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0446\u0435\u043b\u0435\u0432\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443\n            target_values = list(self.tree.item(target_row, \"values\"))\n            target_values[1] = representative  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            target_values[2] = original_result  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435, \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435)\n            target_values[3] = self.mappings.get(representative, representative)  # \u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 (\u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0439)\n            target_values[5] = translated_result  # \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f (\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435, \u043f\u0435\u0440\u0435\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0435)\n            self.tree.item(target_row, values=target_values)\n            # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438\n            for row in rows_to_merge:\n                if row['item'] != target_row:\n                    self.tree.delete(row['item'])\n        print(\"\\n\" + \"\ud83d\udd25\"*80)\n        print(\"\ud83d\udd25 \u041e\u0411\u042a\u0415\u0414\u0418\u041d\u0415\u041d\u0418\u0415 \u0417\u0410\u0412\u0415\u0420\u0428\u0415\u041d\u041e \u0423\u0421\u041f\u0415\u0428\u041d\u041e (\u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043e)\")\n        print(\"\ud83d\udd25\"*80)\n        self.dynamic_update_main_table()\n        self.root.after(1000, self.delayed_check_after_merge)\n\n    def _smart_split_values(self, text, delimiter):\n        \"\"\"\ud83c\udd95 \u0423\u043c\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0432 \u0441\u043a\u043e\u0431\u043a\u0430\u0445 v4.8.5\"\"\"\n        if not text:\n            return []\n        \n        # \ud83c\udd95 \u0417\u0410\u0429\u0418\u0422\u0410 \u041e\u0422 \u041f\u0423\u0421\u0422\u041e\u0413\u041e \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n        if not delimiter:\n            delimiter = \";\"\n            print(f\"\u26a0\ufe0f \u0423\u041c\u041d\u041e\u0415 \u0420\u0410\u0417\u0414\u0415\u041b\u0415\u041d\u0418\u0415: \u043f\u0443\u0441\u0442\u043e\u0439 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u2192 ';'\")\n        \n        # \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u043f\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044e\n        if delimiter and delimiter in text:\n            parts = [p.strip() for p in text.split(delimiter) if p.strip()]\n        elif \"; \" in text:\n            parts = [p.strip() for p in text.split(\"; \") if p.strip()]\n        elif \";\" in text:\n            parts = [p.strip() for p in text.split(\";\") if p.strip()]\n        elif \" / \" in text:\n            parts = [p.strip() for p in text.split(\" / \") if p.strip()]\n        elif \"/\" in text:\n            parts = [p.strip() for p in text.split(\"/\") if p.strip()]\n        else:\n            # \u0415\u0441\u043b\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 \u043d\u0435\u0442, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043a\u0430\u043a \u043e\u0434\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n            parts = [text.strip()] if text.strip() else []\n        \n        # \u041e\u0447\u0438\u0449\u0430\u0435\u043c \u043e\u0442 \u043f\u0443\u0441\u0442\u044b\u0445 \u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0445 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432\n        clean_parts = []\n        for part in parts:\n            if part and part not in [\"/\", \";\", \",\", \"-\"]:\n                clean_parts.append(part)\n        \n        return clean_parts\n\n    def delayed_check_after_merge(self):\n        \"\"\"\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0447\u0435\u0440\u0435\u0437 1 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"\n        print(\"\\n\" + \"\ud83d\udd50\"*100)\n        print(\"\ud83d\udd50 \u041e\u0422\u041b\u041e\u0416\u0415\u041d\u041d\u0410\u042f \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0427\u0415\u0420\u0415\u0417 1 \u0421\u0415\u041a\u0423\u041d\u0414\u0423:\")\n        print(\"\ud83c\udfaf \u0424\u0418\u041d\u0410\u041b\u042c\u041d\u0410\u042f \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0412\u0421\u0415\u0419 \u0422\u0410\u0411\u041b\u0418\u0426\u042b:\")\n        for item in self.tree.get_children():\n            values = self.tree.item(item, \"values\")\n            if \"Multi-Stream\" in values[1] or \"Multi-stream\" in values[1]:\n                print(f\"  \ud83d\udccb {values[1]}: '{values[5]}'\")  # \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440: \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n        print(\"\ud83d\udd50\"*100)\n    \n    def merge_similar_values_in_set(self, values_set):\n        \"\"\"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043d\u0430\u0431\u043e\u0440\u0435, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044f \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u044b\u0435\"\"\"\n        if not values_set:\n            return values_set\n            \n        values_list = list(values_set)\n        return self.merge_similar_values_in_list(values_list)\n    \n    def merge_similar_values_in_list(self, values_list):\n        \"\"\"\u0423\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u043e\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435\"\"\"\n        if not values_list:\n            return []\n        # \ud83c\udd95 \u041d\u041e\u0420\u041c\u0410\u041b\u0418\u0417\u0410\u0426\u0418\u042f \u0424\u041e\u0420\u041c\u0410\u0422\u0410 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u041f\u0415\u0420\u0415\u0414 \u041e\u0411\u042a\u0415\u0414\u0418\u041d\u0415\u041d\u0418\u0415\u041c\n        clean_values = [normalize_value_format(v.strip()) for v in values_list if v and v.strip()]\n        if not clean_values:\n            return []\n        \n        merged_values = []\n        processed = set()\n        \n        # \u041f\u043e\u0434\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u0447\u0430\u0441\u0442\u043e\u0442\u0443 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445\n        value_frequencies = {}\n        for sku_data in self.original_values.values():\n            for param_values in sku_data.values():\n                if param_values:\n                    if isinstance(param_values, str):\n                        # \u0411\u043e\u043b\u0435\u0435 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e\u0435 \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n                        vals = self.split_parameter_values(param_values)\n                        for val in vals:\n                            if val in clean_values:\n                                value_frequencies[val] = value_frequencies.get(val, 0) + 1\n        \n        # \u0413\u0440\u0443\u043f\u043f\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n        similar_groups = {}\n        for value in clean_values:\n            if value in processed:\n                continue\n                \n            group = [value]\n            for other_value in clean_values:\n                if other_value != value and other_value not in processed:\n                    if self.are_values_similar_conservative(value, other_value):\n                        group.append(other_value)\n            \n            # \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b\n            if len(group) > 1:\n                # \u0421\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e \u0447\u0430\u0441\u0442\u043e\u0442\u0435, \u043f\u043e\u0442\u043e\u043c \u043f\u043e \u0434\u043b\u0438\u043d\u0435 (\u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b)\n                representative = max(group, key=lambda x: (\n                    value_frequencies.get(x, 0),  # \u0427\u0430\u0441\u0442\u043e\u0442\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n                    len(x),  # \u0414\u043b\u0438\u043d\u0430 (\u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u043b\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b)\n                    -clean_values.index(x)  # \u041f\u043e\u0440\u044f\u0434\u043e\u043a \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f (\u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439)\n                ))\n            else:\n                representative = group[0]\n            \n            merged_values.append(representative)\n            processed.update(group)\n        \n        # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u043e\u0440\u044f\u0434\u043e\u043a\n        result = []\n        for value in clean_values:\n            if value not in result and value in merged_values:\n                result.append(value)\n            elif value not in result:\n                # \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n                for merged_val in merged_values:\n                    if self.are_values_similar_conservative(value, merged_val):\n                        if merged_val not in result:\n                            result.append(merged_val)\n                        break\n        \n        return result\n    \n    def are_values_similar_advanced(self, val1, val2):\n        \"\"\"\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\"\"\"\n        val1_norm = normalize_variant(val1).lower()\n        val2_norm = normalize_variant(val2).lower()\n        \n        # \u0422\u043e\u0447\u043d\u043e\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\n        if val1_norm == val2_norm:\n            return True\n        \n        # \u041e\u0434\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f \u0432 \u0434\u0440\u0443\u0433\u043e\u043c (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \"Support\" \u0432 \"Supported\")\n        if val1_norm in val2_norm or val2_norm in val1_norm:\n            # \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430: \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0432 \u0434\u043b\u0438\u043d\u0435 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0439\n            len_diff = abs(len(val1_norm) - len(val2_norm))\n            max_len = max(len(val1_norm), len(val2_norm))\n            if max_len > 0 and len_diff / max_len <= 0.3:  # \u041c\u0430\u043a\u0441\u0438\u043c\u0443\u043c 30% \u0440\u0430\u0437\u043d\u0438\u0446\u044b\n                return True\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0438 \u0434\u043b\u044f \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        if max(len(val1_norm), len(val2_norm)) <= 10:\n            return self.check_typo_similarity(val1_norm, val2_norm)\n        \n        return False\n    \n    def are_values_similar_conservative(self, val1, val2):\n        \"\"\"\u041a\u043e\u043d\u0441\u0435\u0440\u0432\u0430\u0442\u0438\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 - \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0440\u043e\u0433\u0430\u044f\"\"\"\n        if not val1 or not val2:\n            return False\n            \n        val1_norm = normalize_variant(val1).lower().strip()\n        val2_norm = normalize_variant(val2).lower().strip()\n        \n        # \u0422\u043e\u0447\u043d\u043e\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\n        if val1_norm == val2_norm:\n            return True\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b\n        # \u041e\u0434\u0438\u043d \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0441\u0442\u0440\u043e\u043a\u043e\u0439 \u0434\u0440\u0443\u0433\u043e\u0433\u043e, \u043d\u043e \u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f\u043c\u0438\n        if len(val1_norm) >= 3 and len(val2_norm) >= 3:\n            # \u0420\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043e\u0434\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0447\u0442\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0440\u0443\u0433\u043e\u0435\n            if val1_norm in val2_norm:\n                # \u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u043d\u0435 \u0431\u043e\u043b\u0435\u0435 20% \u043e\u0442 \u0434\u043b\u0438\u043d\u044b \u0431\u043e\u043b\u044c\u0448\u0435\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n                if (len(val2_norm) - len(val1_norm)) / len(val2_norm) <= 0.2:\n                    return True\n            elif val2_norm in val1_norm:\n                if (len(val1_norm) - len(val2_norm)) / len(val1_norm) <= 0.2:\n                    return True\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u044f\u0432\u043d\u044b\u0445 \u043e\u043f\u0435\u0447\u0430\u0442\u043e\u043a \u0434\u043b\u044f \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        if 3 <= max(len(val1_norm), len(val2_norm)) <= 8:\n            # \u0422\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0430 \u0437\u0430\u043c\u0435\u043d\u0430 \u0441\u0438\u043c\u0432\u043e\u043b\u0430\n            if abs(len(val1_norm) - len(val2_norm)) <= 1:\n                from difflib import SequenceMatcher\n                similarity = SequenceMatcher(None, val1_norm, val2_norm).ratio()\n                if similarity >= 0.85:  # \u041e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u044c\n                    return True\n        \n        return False\n\n    def split_parameter_values(self, param_values):\n        \"\"\"\u0411\u043e\u043b\u0435\u0435 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e\u0435 \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        if not param_values:\n            return []\n        \n        # \u041f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0437\u043d\u044b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438\n        for delimiter in [';', ',', '|']:\n            if delimiter in param_values:\n                values = [v.strip() for v in param_values.split(delimiter) if v.strip()]\n                if len(values) > 1:\n                    return values\n        \n        # \u0415\u0441\u043b\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 \u043d\u0435\u0442, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043a\u0430\u043a \u0435\u0441\u0442\u044c\n        return [param_values.strip()] if param_values.strip() else []\n\n    def are_values_similar(self, val1, val2):\n        \"\"\"\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043b\u0438 \u0434\u0432\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u0445\u043e\u0436\u0438\u043c\u0438\"\"\"\n        # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u043f\u0443\u043d\u043a\u0442\u0443\u0430\u0446\u0438\u044e \u0438 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u043c \u043a \u043d\u0438\u0436\u043d\u0435\u043c\u0443 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0443\n        import string\n        val1_clean = val1.translate(str.maketrans('', '', string.punctuation)).replace(' ', '')\n        val2_clean = val2.translate(str.maketrans('', '', string.punctuation)).replace(' ', '')\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u0438 \u043e\u0434\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u0440\u0443\u0433\u043e\u0435\n        if val1_clean in val2_clean or val2_clean in val1_clean:\n            return True\n            \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u044c \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e SequenceMatcher\n        similarity = SequenceMatcher(None, val1_clean, val2_clean).ratio()\n        return similarity > 0.8  # 80% \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438\n\n    def apply_value_merges(self, merge_data):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        if not merge_data:\n            return\n            \n        # \u041f\u0440\u043e\u0445\u043e\u0434\u0438\u043c \u043f\u043e \u0432\u0441\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u043c \u0434\u0435\u0440\u0435\u0432\u0430 \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        for item in self.tree.get_children():\n            values = list(self.tree.item(item, \"values\"))\n            param = values[1]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            orig_values = values[2]  # \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n            result_values = values[5]  # \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\n            \n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n            if param in merge_data:\n                param_merges = merge_data[param]\n                \n                # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043a \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\n                if orig_values:\n                    merged_orig_values = self.apply_value_merges_to_string(orig_values, param_merges)\n                    values[2] = merged_orig_values\n                \n                # \u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043a \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\n                if result_values:\n                    merged_result_values = self.apply_value_merges_to_string(result_values, param_merges)\n                    values[5] = merged_result_values\n                \n                # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0434\u0435\u0440\u0435\u0432\u0430\n                self.tree.item(item, values=values)\n        \n        # \u0412\u0420\u0415\u041c\u0415\u041d\u041d\u041e \u0423\u0411\u0420\u0410\u041d\u041e: \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 DataFrame \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b  \n        # self.df = pd.DataFrame([{\n        #     \"\u2116\": int(values[0]),\n        #     \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\": values[1],\n        #     \"\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\": values[2],\n        #     \"\u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\": values[3],\n        #     \"\u0420\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c\": values[4],\n        #     \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\": values[5]\n        # } for item in self.tree.get_children() for values in [self.tree.item(item, \"values\")]])\n        \n        # \u0423\u0411\u0420\u0410\u041d\u041e: self.refresh_treeview_with_colors() - \u0412\u0422\u041e\u0420\u0410\u042f \u0424\u0423\u041d\u041a\u0426\u0418\u042f \u0442\u043e\u0436\u0435 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u043b\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b!\n        # \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0446\u0432\u0435\u0442\u043e\u0432 \u043d\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        \n        # \ud83c\udd95 \u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 v4.9\n        print(\"\ud83d\udd04 \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439...\")\n        self.dynamic_update_main_table()\n    \n    def apply_value_merges_to_string(self, values_string, param_merges):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043a \u0441\u0442\u0440\u043e\u043a\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\"\"\n        if not values_string or not param_merges:\n            return values_string\n            \n        # \u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u043d\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n        values_list = [v.strip() for v in values_string.split(';') if v.strip()]\n        merged_values = []\n        processed_values = set()\n        \n        for value in values_list:\n            if value in processed_values:\n                continue\n                \n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0443\u0436\u043d\u043e \u043b\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n            merged = False\n            for representative, merge_info in param_merges.items():\n                if merge_info.get('selected', False):\n                    similar_values = merge_info.get('similar_values', [])\n                    if value in similar_values:\n                        # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b, \u0435\u0441\u043b\u0438 \u0435\u0433\u043e \u0435\u0449\u0435 \u043d\u0435\u0442\n                        if representative not in merged_values:\n                            merged_values.append(representative)\n                        processed_values.update(similar_values)\n                        merged = True\n                        break\n            \n            # \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435 \u0431\u044b\u043b\u043e \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u043e, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0433\u043e \u043a\u0430\u043a \u0435\u0441\u0442\u044c\n            if not merged:\n                merged_values.append(value)\n                processed_values.add(value)\n        \n        return \"; \".join(merged_values)\n\n    def clean_cell(self, value):\n        \"\"\"\ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0423\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u044f\u0447\u0435\u0439\u043a\u0438\"\"\"\n        if not isinstance(value, str):\n            return value\n        value = value.strip()\n        \n        # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0447\u0438\u0441\u0442\u043a\u0438\n        return clean_value_enhanced(value)\n\n    def remove_duplicates_from_cell(self, cell_value):\n        \"\"\"\ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0423\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u043e\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432 \u0438\u0437 \u044f\u0447\u0435\u0439\u043a\u0438\"\"\"\n        if not cell_value:\n            return cell_value\n        seps = [',', ';']\n        sep = None\n        for s in seps:\n            if s in cell_value:\n                sep = s\n                break\n        if sep is None:\n            return cell_value\n        \n        items = [self.clean_cell(v) for v in cell_value.split(sep)]\n        seen = set()\n        unique = []\n        \n        for v in items:\n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n            if v and is_valid_value_enhanced(v):\n                norm_v = normalize_variant(v).lower()\n                if norm_v not in seen:\n                    seen.add(norm_v)\n                    unique.append(v)\n        \n        return sep.join(unique) if unique else \"\"\n\n    def remove_duplicates_from_list(self, values):\n        seen = set()\n        result = []\n        removed_duplicates = []\n        \n        for v in values:\n            if v:\n                norm_v = normalize_variant(v).lower()\n                if norm_v not in seen:\n                    seen.add(norm_v)\n                    result.append(v)\n                else:\n                    removed_duplicates.append(v)\n        \n        if removed_duplicates:\n            print(f\"    \ud83d\uddd1\ufe0f \u0423\u0414\u0410\u041b\u0415\u041d\u042b \u0414\u0423\u0411\u041b\u0418: {removed_duplicates} (\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b: {result})\")\n        \n        return result\n\n    def parse_html_table(self, html):\n        \"\"\"\ud83c\udd95 \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u042b\u0419 \u041f\u0410\u0420\u0421\u0415\u0420 HTML-\u0422\u0410\u0411\u041b\u0418\u0426 \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u043e\u043a\u043e\u043b\u043e\u043d\u043e\u0447\u043d\u044b\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\n        \n        \ud83d\udd27 \u0412\u0410\u0416\u041d\u041e: \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0418\u0421\u0425\u041e\u0414\u041d\u042b\u0415 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430!\n        \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.\n        \"\"\"\n        soup = BeautifulSoup(html, 'html.parser')\n        params = {}\n        current_param = None\n        \n        for tr in soup.find_all('tr'):\n            tds = tr.find_all('td')\n\n            if len(tds) == 0:\n                continue\n\n            # \ud83c\udd95 \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0410 \u0421\u0422\u0420\u041e\u041a \u0421 \u041e\u0414\u041d\u041e\u0419 \u042f\u0427\u0415\u0419\u041a\u041e\u0419\n            if len(tds) == 1:\n                colspan = tds[0].get('colspan')\n                try:\n                    colspan_num = int(colspan) if colspan else 1\n                except:\n                    colspan_num = 1\n                \n                cell_text = self.clean_cell(tds[0].get_text())\n                \n                # \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441\u0435\u043a\u0446\u0438\u0438 (colspan >= 2), \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c\n                if colspan_num >= 2:\n                    current_param = None\n                    continue\n                    \n                # \u0415\u0441\u043b\u0438 \u044d\u0442\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n                if current_param and cell_text:\n                    # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n                    params[current_param] += f\"; {cell_text}\"\n                continue\n\n            # \ud83c\udd95 \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0410 \u041c\u041d\u041e\u0413\u041e\u041a\u041e\u041b\u041e\u041d\u041e\u0427\u041d\u042b\u0425 \u0421\u0422\u0420\u041e\u041a (2+ \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432)\n            if len(tds) >= 2:\n                # \u041f\u0435\u0440\u0432\u044b\u0439 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 - \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n                param_cell = tds[0]\n                param = self.clean_cell(param_cell.get_text())\n                \n                # \u0415\u0441\u043b\u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u0442\u043e\u043b\u0431\u0435\u0446 \u043f\u0443\u0441\u0442\u043e\u0439 \u0438\u043b\u0438 \u044d\u0442\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441 colspan, \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c\n                if not param:\n                    current_param = None\n                    continue\n                \n                param_colspan = param_cell.get('colspan')\n                try:\n                    param_colspan_num = int(param_colspan) if param_colspan else 1\n                except:\n                    param_colspan_num = 1\n                \n                # \u0415\u0441\u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432, \u044d\u0442\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441\u0435\u043a\u0446\u0438\u0438\n                if param_colspan_num >= 2:\n                    current_param = None\n                    continue\n                \n                # \ud83c\udd95 \u0421\u041e\u0411\u0418\u0420\u0410\u0415\u041c \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u042f \u0418\u0417 \u0412\u0421\u0415\u0425 \u041e\u0421\u0422\u0410\u041b\u042c\u041d\u042b\u0425 \u0421\u0422\u041e\u041b\u0411\u0426\u041e\u0412\n                values = []\n                for i in range(1, len(tds)):\n                    value_cell = tds[i]\n                    value = self.clean_cell(value_cell.get_text())\n                    \n                    if value:\n                        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c colspan \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n                        value_colspan = value_cell.get('colspan')\n                        try:\n                            value_colspan_num = int(value_colspan) if value_colspan else 1\n                        except:\n                            value_colspan_num = 1\n                        \n                        values.append(value)\n                        \n                        # \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432, \n                        # \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u044f\u0447\u0435\u0435\u043a\n                        if value_colspan_num > 1:\n                            i += value_colspan_num - 1\n                \n                # \ud83c\udd95 \u041e\u0411\u042a\u0415\u0414\u0418\u041d\u042f\u0415\u041c \u0412\u0421\u0415 \u041d\u0410\u0419\u0414\u0415\u041d\u041d\u042b\u0415 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u042f\n                if param and values:\n                    # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439, \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u044f \u043f\u043e\u0440\u044f\u0434\u043e\u043a\n                    unique_values = []\n                    seen = set()\n                    for val in values:\n                        val_normalized = val.lower().strip()\n                        if val_normalized not in seen:\n                            seen.add(val_normalized)\n                            unique_values.append(val)\n                    \n                    combined_value = \"; \".join(unique_values)\n                    \n                    # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n                    params[param] = combined_value\n                    current_param = param\n                else:\n                    current_param = None\n                continue\n\n            current_param = None\n            \n        return params\n\n    def find_similar_param_groups(self, param_series):\n        \"\"\"\n        \ud83d\udea8 \u0417\u0410\u0411\u041b\u041e\u041a\u0418\u0420\u041e\u0412\u0410\u041d\u041d\u0410\u042f \u0421\u0422\u0410\u0420\u0410\u042f \u0424\u0423\u041d\u041a\u0426\u0418\u042f - \u041d\u0415 \u0418\u0421\u041f\u041e\u041b\u042c\u0417\u041e\u0412\u0410\u0422\u042c!\n        \n        \u042d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0431\u044b\u043b\u0430 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u043e\u043c \u043e\u0448\u0438\u0431\u043e\u0447\u043d\u043e\u0439 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 Weight + Night.\n        \u0417\u0430\u043c\u0435\u043d\u0435\u043d\u0430 \u043d\u0430 find_similar_parameters_advanced().\n        \"\"\"\n        print(\"\ud83d\udea8 \u0412\u041d\u0418\u041c\u0410\u041d\u0418\u0415: \u0412\u044b\u0437\u0432\u0430\u043d\u0430 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0441\u0442\u0430\u0440\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f find_similar_param_groups!\")\n        print(\"\ud83d\udea8 \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 find_similar_parameters_advanced() \u0432\u043c\u0435\u0441\u0442\u043e \u043d\u0435\u0451!\")\n        \n        # \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438 \u043f\u043e \u0441\u0442\u0430\u0440\u044b\u043c \u043a\u0440\u0438\u0442\u0435\u0440\u0438\u044f\u043c\n        return {param: 0 for param in param_series}\n\n    def update_mappings_from_params(self, params):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u043e\u0432 \u0438\u0437 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        for param in params:\n            if param not in self.mappings:\n                self.mappings[param] = param\n\n    # \u274c \u0423\u0414\u0410\u041b\u0415\u041d \u043c\u0435\u0442\u043e\u0434 replace_mapped_values_in_str - \u0418\u0421\u0422\u041e\u0427\u041d\u0418\u041a \u0427\u0410\u0421\u0422\u0418\u0427\u041d\u041e\u0413\u041e \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u0410\n    # \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u043b \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043a \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c, \u0447\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u043e \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\n    # \u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u0422\u041e\u041b\u042c\u041a\u041e \u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\n\n    def apply_case_normalization(self, value):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\"\"\"\n        if not value or not isinstance(value, str):\n            return value\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u043e\u0447\u043d\u043e\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435\n        if value in self.case_normalization:\n            return self.case_normalization[value]\n        \n        # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0435\u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043a \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0443 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435\n        for norm_value, replacement in self.case_normalization.items():\n            if value.lower() == norm_value.lower():\n                return replacement\n        \n        return value\n\n    def apply_case_normalization_in_str(self, value, delimiter=';'):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0435\"\"\"\n        if not value:\n            return value\n        \n        parts = [part.strip() for part in value.split(delimiter)]\n        normalized_parts = [self.apply_case_normalization(part) for part in parts]\n        \n        return delimiter.join(normalized_parts)\n\n    def split_values(self, value, delimiter):\n        \"\"\"\n        \ud83c\udd95 \u0423\u041b\u0423\u0427\u0428\u0415\u041d\u041d\u041e\u0415 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        \n        \u041d\u041e\u0412\u042b\u0415 \u0412\u041e\u0417\u041c\u041e\u0416\u041d\u041e\u0421\u0422\u0418:\n        - \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0435\u0439 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \",/\" \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e \",\" \u0418 \u043f\u043e \"/\")\n        - \ud83d\udee1\ufe0f \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0415 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u042f: \u043d\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0441\u0438\u043c\u0432\u043e\u043b \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f\n        - \u041f\u0440\u0438\u043c\u0435\u0440: \"Auto (ICR) / Color / B/W\" \u0441 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u043c \"B/W\" \u2192 [\"Auto (ICR)\", \"Color\", \"B/W\"]\n        \"\"\"\n        if not value:\n            return []\n        \n        # \ud83c\udd95 \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n        if not delimiter:\n            print(\"\u26a0\ufe0f \u041f\u0423\u0421\u0422\u041e\u0419 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042c - \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 ';'\")\n            delimiter = \";\"\n        \n        # \ud83d\udee1\ufe0f \u0417\u0410\u0429\u0418\u0422\u0410 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u041e\u0422 \u0420\u0410\u0417\u0414\u0415\u041b\u0415\u041d\u0418\u042f\n        protected_replacements = {}\n        modified_value = value\n        \n        if self.protected_values:\n            print(f\"\ud83d\udee1\ufe0f \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419: {self.protected_values}\")\n            \n            for i, protected in enumerate(self.protected_values):\n                if protected in modified_value:\n                    placeholder = f\"__PROTECTED_{i}__\"\n                    protected_replacements[placeholder] = protected\n                    modified_value = modified_value.replace(protected, placeholder)\n                    print(f\"  \ud83d\udd12 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041e: '{protected}' \u2192 '{placeholder}'\")\n            \n            print(f\"  \ud83d\udccb \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0449\u0438\u0442\u044b: '{modified_value}'\")\n        \n        # \ud83d\udd0d \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u041a\u0410 \u041c\u041d\u041e\u0416\u0415\u0421\u0422\u0412\u0415\u041d\u041d\u042b\u0425 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u0415\u0419\n        if len(delimiter) > 1 and delimiter != \"; \":\n            # \u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440: \",/\" \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e \",\" \u0418 \"/\")\n            import re\n            \n            # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0432\u044b\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043e \u043b\u044e\u0431\u043e\u043c\u0443 \u0438\u0437 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432\n            # \u042d\u043a\u0440\u0430\u043d\u0438\u0440\u0443\u0435\u043c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b \u0434\u043b\u044f regex\n            escaped_chars = [re.escape(char) for char in delimiter]\n            pattern = '[' + ''.join(escaped_chars) + ']'\n            \n            print(f\"\ud83d\udd0d \u041c\u041d\u041e\u0416\u0415\u0421\u0422\u0412\u0415\u041d\u041d\u042b\u0415 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u0418: '{delimiter}' \u2192 pattern: '{pattern}'\")\n            result_values = [v.strip() for v in re.split(pattern, modified_value) if v.strip()]\n        else:\n            # \u041e\u0431\u044b\u0447\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044e\n            # \ud83c\udd95 \u0414\u041e\u041f\u041e\u041b\u041d\u0418\u0422\u0415\u041b\u042c\u041d\u0410\u042f \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0420\u0410\u0417\u0414\u0415\u041b\u0418\u0422\u0415\u041b\u042f v4.8.5\n            if not delimiter:\n                delimiter = \";\"\n                print(\"\u26a0\ufe0f \u0410\u0412\u0410\u0420\u0418\u0419\u041d\u0410\u042f \u0417\u0410\u041c\u0415\u041d\u0410: \u043f\u0443\u0441\u0442\u043e\u0439 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044c \u2192 ';'\")\n            \n            result_values = [v.strip() for v in modified_value.split(delimiter) if v.strip()]\n        \n        # \ud83d\udd13 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u0415 \u0417\u0410\u0429\u0418\u0429\u0415\u041d\u041d\u042b\u0425 \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419\n        if protected_replacements:\n            restored_values = []\n            for val in result_values:\n                if val in protected_replacements:\n                    restored_val = protected_replacements[val]\n                    print(f\"  \ud83d\udd13 \u0412\u041e\u0421\u0421\u0422\u0410\u041d\u041e\u0412\u041b\u0415\u041d\u041e: '{val}' \u2192 '{restored_val}'\")\n                    restored_values.append(restored_val)\n                else:\n                    restored_values.append(val)\n            result_values = restored_values\n        \n        # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041e\u0447\u0438\u0441\u0442\u043a\u0430 \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n        cleaned_values = []\n        for val in result_values:\n            cleaned = clean_value_enhanced(val)\n            if is_valid_value_enhanced(cleaned):\n                cleaned_values.append(cleaned)\n            else:\n                print(f\"  \ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0435\u043d\u043e \u043d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: '{val}'\")\n        \n        # \ud83c\udd95 \u0423\u0414\u0410\u041b\u0415\u041d\u0418\u0415 \u0414\u0423\u0411\u041b\u0418\u041a\u0410\u0422\u041e\u0412 \u041f\u041e\u0421\u041b\u0415 \u0420\u0410\u0417\u0414\u0415\u041b\u0415\u041d\u0418\u042f\n        # \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u0435\u0442\u043e\u0434 remove_duplicates_from_list, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f\n        result_values = self.remove_duplicates_from_list(cleaned_values)\n        \n        print(f\"  \u2705 \u0424\u0418\u041d\u0410\u041b\u042c\u041d\u042b\u0419 \u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422 (\u043e\u0447\u0438\u0449\u0435\u043d\u043d\u044b\u0439, \u0431\u0435\u0437 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432): {result_values}\")\n        return result_values\n\n    def refresh_treeview_with_colors(self):\n        \"\"\"\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0441\u043a\u0440\u0430\u0441\u043a\u0438 \u0434\u0435\u0440\u0435\u0432\u0430\"\"\"\n        # \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0442\u0435\u0433\u0438 \u0434\u043b\u044f \u0440\u0430\u0441\u043a\u0440\u0430\u0441\u043a\u0438\n        colors = ['#f0f0f0', '#e0e0e0', '#d0d0d0', '#c0c0c0', '#b0b0b0']\n        \n        # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043f\u043e \u0438\u0445 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438\n        group_colors = {}\n        color_index = 0\n        \n        for item in self.tree.get_children():\n            values = self.tree.item(item, \"values\")\n            tags = self.tree.item(item, \"tags\")\n            \n            if tags:\n                group_tag = tags[0]\n                if group_tag not in group_colors:\n                    group_colors[group_tag] = colors[color_index % len(colors)]\n                    color_index += 1\n                \n                # \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0442\u0435\u0433 \u0441 \u0446\u0432\u0435\u0442\u043e\u043c\n                self.tree.tag_configure(group_tag, background=group_colors[group_tag])\n            else:\n                # \u0414\u043b\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0431\u0435\u0437 \u0433\u0440\u0443\u043f\u043f\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0431\u0435\u043b\u044b\u0439 \u0444\u043e\u043d\n                self.tree.tag_configure(\"no_group\", background=\"white\")\n                self.tree.item(item, tags=(\"no_group\",))\n\n    def auto_clean_duplicate_values(self):\n        \"\"\"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0430 \u0434\u0443\u0431\u043b\u0438\u043a\u0430\u0442\u043e\u0432\"\"\"\n        # \u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\n        return 0\n\n    def auto_suggest_parameter_merge(self):\n        \"\"\"\ud83d\udea8 \u041e\u0422\u041a\u041b\u042e\u0427\u0415\u041d\u0410: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        \n        \u042d\u0422\u0410 \u0424\u0423\u041d\u041a\u0426\u0418\u042f \u0421\u041e\u0417\u0414\u0410\u0412\u0410\u041b\u0410 \u0414\u0423\u0411\u041b\u0418\u0420\u041e\u0412\u0410\u041d\u0418\u0415 \u0412 param_merge_settings!\n        \u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e find_similar_parameters_for_tab()\n        \"\"\"\n        print(\"\ud83d\udea8 \u0424\u0423\u041d\u041a\u0426\u0418\u042f auto_suggest_parameter_merge \u041e\u0422\u041a\u041b\u042e\u0427\u0415\u041d\u0410 - \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f!\")\n        return\n        \n        # \u0421\u0422\u0410\u0420\u042b\u0419 \u041a\u041e\u0414 \u0417\u0410\u041a\u041e\u041c\u041c\u0415\u041d\u0422\u0418\u0420\u041e\u0412\u0410\u041d\n        # if not hasattr(self, 'similar_groups') or not self.similar_groups:\n        #     return\n        # \n        # # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        # param_groups = {}\n        # for param, group_id in self.similar_groups.items():\n        #     if group_id not in param_groups:\n        #         param_groups[group_id] = []\n        #     param_groups[group_id].append(param)\n        # \n        # # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0433\u0440\u0443\u043f\u043f \u0441 \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043e\u0434\u043d\u0438\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c\n        # suggestions = []\n        # for group_id, params in param_groups.items():\n        #     if len(params) > 1:\n        #         # \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f\n        #         param_frequencies = {}\n        #         for param in params:\n        #             count = 0\n        #             for sku_data in self.original_values.values():\n        #                 if param in sku_data:\n        #                     count += 1\n        #             param_frequencies[param] = count\n        #         \n        #         if param_frequencies:\n        #             representative = max(param_frequencies.items(), key=lambda x: x[1])[0]\n        #             suggestions.append((representative, params))\n        # \n        # # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        # for representative, similar_params in suggestions:\n        #     self.param_merge_settings[representative] = {\n        #         'similar_params': similar_params,\n        #         'selected': False\n        #     }\n        # \n        # # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0434\u0435\u0440\u0435\u0432\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\n        # if hasattr(self, 'params_tree'):\n        #     self.update_params_tree()\n\n    def update_param_frequency(self, params):\n        \"\"\"\u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041d\u041e\u0415 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0447\u0430\u0441\u0442\u043e\u0442\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        print(\"\ud83d\udcca \u041f\u041e\u0414\u0421\u0427\u0415\u0422 \u0427\u0410\u0421\u0422\u041e\u0422\u042b \u0418\u0421\u041f\u041e\u041b\u042c\u0417\u041e\u0412\u0410\u041d\u0418\u042f \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412\")\n        self.param_frequency = {}\n        \n        for param in params:\n            count = 0\n            for sku_params in self.original_values.values():\n                if param in sku_params:\n                    count += 1\n            self.param_frequency[param] = count\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0443 \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438\n        total_params = len(params)\n        print(f\"\ud83d\udccb \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {total_params}\")\n        \n        if total_params > 0:\n            avg_frequency = sum(self.param_frequency.values()) / total_params\n            max_frequency = max(self.param_frequency.values()) if self.param_frequency else 0\n            print(f\"\ud83d\udcca \u0421\u0440\u0435\u0434\u043d\u044f\u044f \u0447\u0430\u0441\u0442\u043e\u0442\u0430: {avg_frequency:.1f}\")\n            print(f\"\ud83d\udcca \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u043e\u0442\u0430: {max_frequency}\")\n            \n            # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u044b Multi \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438\n            multi_params = [p for p in params if 'multi' in p.lower()]\n            if multi_params:\n                print(\"\ud83d\udd0d Multi \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0438\u0445 \u0447\u0430\u0441\u0442\u043e\u0442\u0430:\")\n                for param in sorted(multi_params, key=lambda x: self.param_frequency.get(x, 0), reverse=True):\n                    freq = self.param_frequency.get(param, 0)\n                    print(f\"  \u2022 {param}: {freq} \u0440\u0430\u0437\")\n\n    def find_similar_parameters_advanced(self, params):\n        \"\"\"\n        \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041d\u042b\u0419 \u043f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u0438\n        \n        \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u0418\u042f:\n        - \u041f\u043e\u0432\u044b\u0448\u0435\u043d\u044b \u043f\u043e\u0440\u043e\u0433\u0438 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438 \u0434\u043b\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u043b\u043e\u0436\u043d\u044b\u0445 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u043d\u0438\u0439\n        - \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u043e\u0445\u043e\u0436\u0435\u0441\u0442\u044c (\u043e\u0431\u0449\u0438\u0435 \u0441\u043b\u043e\u0432\u0430)\n        - \u0418\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u043b\u043e\u0433\u0438\u043a\u0430 \u0432\u044b\u0431\u043e\u0440\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043f\u043e \u0447\u0430\u0441\u0442\u043e\u0442\u0435\n        - \u0423\u0431\u0440\u0430\u043d\u043e \u0434\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n        \"\"\"\n        print(\"\ud83d\udd0d \u041f\u041e\u0418\u0421\u041a \u041f\u041e\u0425\u041e\u0416\u0418\u0425 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412 - \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041d\u0410\u042f \u0412\u0415\u0420\u0421\u0418\u042f\")\n        print(f\"\ud83d\udcca \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438: {len(params)}\")\n        \n        similar_groups = {}\n        processed = set()\n        \n        for i, param1 in enumerate(params):\n            if param1 in processed:\n                continue\n                \n            group = [param1]\n            \n            for j, param2 in enumerate(params[i+1:], i+1):\n                if param2 in processed:\n                    continue\n                \n                # \u0411\u041e\u041b\u0415\u0415 \u0421\u0422\u0420\u041e\u0413\u0418\u0415 \u041a\u0420\u0418\u0422\u0415\u0420\u0418\u0418 \u0421\u0425\u041e\u0416\u0415\u0421\u0422\u0418\n                is_similar = self.are_parameters_truly_similar(param1, param2)\n                \n                if is_similar:\n                    group.append(param2)\n                    print(f\"  \u2705 \u041d\u0430\u0439\u0434\u0435\u043d\u0430 \u043f\u043e\u0445\u043e\u0436\u0430\u044f \u043f\u0430\u0440\u0430: '{param1}' \u2194 '{param2}'\")\n            \n            if len(group) > 1:\n                # \ud83c\udd95 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u0418\u0415 v4.9.2: \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441 \u041d\u0410\u0418\u0411\u041e\u041b\u042c\u0428\u0418\u041c \u041a\u041e\u041b\u0418\u0427\u0415\u0421\u0422\u0412\u041e\u041c \u0417\u041d\u0410\u0427\u0415\u041d\u0418\u0419 \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f\n                def get_value_count(param_name):\n                    \"\"\"\u041f\u043e\u0434\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435\"\"\"\n                    total_values = 0\n                    for sku_params in self.original_values.values():\n                        if param_name in sku_params:\n                            values = sku_params[param_name]\n                            if isinstance(values, list):\n                                total_values += len([v for v in values if v.strip()])\n                            elif isinstance(values, str) and values.strip():\n                                # \u0421\u0447\u0438\u0442\u0430\u0435\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u043a\u043e\u0439 \u0441 \u0437\u0430\u043f\u044f\u0442\u043e\u0439\n                                parts = [v.strip() for v in values.split(';') if v.strip()]\n                                total_values += len(parts)\n                    return total_values\n                \n                # \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0441 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n                value_counts = {}\n                for param in group:\n                    value_counts[param] = get_value_count(param)\n                \n                representative = max(group, key=lambda x: value_counts.get(x, 0))\n                \n                # \ud83d\udd25 \u0423\u0411\u0418\u0420\u0410\u0415\u041c \u0418\u0417 \u0413\u0420\u0423\u041f\u041f\u042b \u041f\u0420\u0415\u0414\u0421\u0422\u0410\u0412\u0418\u0422\u0415\u041b\u042f - \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0445\u043e\u0436\u0438\u0435\n                other_params = [p for p in group if p != representative]\n                \n                print(f\"\ud83c\udfaf \u0413\u0420\u0423\u041f\u041f\u0410 \u041d\u0410\u0419\u0414\u0415\u041d\u0410:\")\n                print(f\"  \ud83d\udc51 \u041e\u0421\u041d\u041e\u0412\u041d\u041e\u0419: {representative}\")\n                print(f\"      \ud83d\udcca \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {value_counts.get(representative, 0)}\")\n                print(f\"      \ud83d\udcc8 \u0427\u0430\u0441\u0442\u043e\u0442\u0430: {self.param_frequency.get(representative, 0)}\")\n                \n                for param in other_params:\n                    values_count = value_counts.get(param, 0)\n                    freq = self.param_frequency.get(param, 0)\n                    print(f\"     \u21b3 {param}\")\n                    print(f\"       \ud83d\udcca \u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {values_count}\")\n                    print(f\"       \ud83d\udcc8 \u0427\u0430\u0441\u0442\u043e\u0442\u0430: {freq}\")\n                \n                # \ud83d\udd25 \u041a\u041b\u042e\u0427\u0415\u0412\u041e\u0415 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u0418\u0415: \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u2192 \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0445\n                # \ud83d\udd25 \u0414\u041e\u041f\u041e\u041b\u041d\u0418\u0422\u0415\u041b\u042c\u041d\u0410\u042f \u0417\u0410\u0429\u0418\u0422\u0410: \u0423\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f \u0435\u0441\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043f\u043e\u043f\u0430\u043b\n                clean_other_params = [p for p in other_params if p != representative]\n                similar_groups[representative] = clean_other_params\n                \n                print(f\"  \ud83d\udd0d \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0414\u0423\u0411\u041b\u0418\u0420\u041e\u0412\u0410\u041d\u0418\u042f:\")\n                print(f\"     \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044c: {representative}\")\n                print(f\"     \u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445: {clean_other_params}\")\n                if representative in clean_other_params:\n                    print(f\"  \ud83d\udea8 \u041e\u0428\u0418\u0411\u041a\u0410: \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044c \u043d\u0430\u0439\u0434\u0435\u043d \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u043f\u043e\u0445\u043e\u0436\u0438\u0445!\")\n                else:\n                    print(f\"  \u2705 \u041e\u041a: \u0414\u0443\u0431\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0435\u0442\")\n                \n                # \ud83d\udd25 \u041e\u0422\u041c\u0415\u0427\u0410\u0415\u041c \u0412\u0421\u0415 \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u042b \u0413\u0420\u0423\u041f\u041f\u042b \u041a\u0410\u041a \u041e\u0411\u0420\u0410\u0411\u041e\u0422\u0410\u041d\u041d\u042b\u0415\n                processed.update(group)\n            else:\n                # \u0415\u0441\u043b\u0438 \u0433\u0440\u0443\u043f\u043f\u0430 \u0438\u0437 \u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 - \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043c\u0435\u0447\u0430\u0435\u043c \u043a\u0430\u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0439\n                processed.add(param1)\n        \n        print(f\"\ud83d\udcca \u0418\u0422\u041e\u0413\u041e \u041d\u0410\u0419\u0414\u0415\u041d\u041e \u0413\u0420\u0423\u041f\u041f: {len(similar_groups)}\")\n        return similar_groups\n    \n    def are_parameters_truly_similar(self, param1, param2):\n        \"\"\"\n        \ud83d\udea8 \u042d\u041a\u0421\u0422\u0420\u0415\u041c\u0410\u041b\u042c\u041d\u0410\u042f \u041e\u0422\u041b\u0410\u0414\u041a\u0410: \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043f\u043e\u0445\u043e\u0436\u0438\n        \"\"\"\n        print(f\"\\n\ud83d\udd0d \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u0421\u0425\u041e\u0416\u0415\u0421\u0422\u0418: '{param1}' \u2194 '{param2}'\")\n        \n        param1_lower = param1.lower().strip()\n        param2_lower = param2.lower().strip()\n        \n        # 1. \u0422\u041e\u0427\u041d\u041e\u0415 \u0421\u041e\u0412\u041f\u0410\u0414\u0415\u041d\u0418\u0415 (\u0440\u0430\u0437\u043d\u044b\u0439 \u0440\u0435\u0433\u0438\u0441\u0442\u0440)\n        if param1_lower == param2_lower:\n            print(f\"  \u2705 \u0422\u041e\u0427\u041d\u041e\u0415 \u0421\u041e\u0412\u041f\u0410\u0414\u0415\u041d\u0418\u0415\")\n            return True\n        \n        # 2. \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u041d\u0410 \u041e\u0411\u0429\u0418\u0415 \u041a\u041b\u042e\u0427\u0415\u0412\u042b\u0415 \u0421\u041b\u041e\u0412\u0410\n        words1 = set(re.findall(r'\\b\\w+\\b', param1_lower))\n        words2 = set(re.findall(r'\\b\\w+\\b', param2_lower))\n        \n        print(f\"  \ud83d\udccb \u0421\u043b\u043e\u0432\u04301: {words1}\")\n        print(f\"  \ud83d\udccb \u0421\u043b\u043e\u0432\u04302: {words2}\")\n        \n        # \u0418\u0441\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043e\u0447\u0435\u043d\u044c \u043e\u0431\u0449\u0438\u0435 \u0441\u043b\u043e\u0432\u0430\n        common_words = {'and', 'or', 'the', 'a', 'an', 'is', 'are', 'in', 'on', 'at', 'to', 'for', 'of', 'with'}\n        words1_filtered = words1 - common_words\n        words2_filtered = words2 - common_words\n        \n        print(f\"  \ud83d\udccb \u0421\u043b\u043e\u0432\u04301 (\u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440): {words1_filtered}\")\n        print(f\"  \ud83d\udccb \u0421\u043b\u043e\u0432\u04302 (\u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440): {words2_filtered}\")\n        \n        if not words1_filtered or not words2_filtered:\n            print(f\"  \u274c \u041e\u0422\u041a\u041b\u041e\u041d\u0415\u041d\u041e: \u041f\u0443\u0441\u0442\u044b\u0435 \u0441\u043b\u043e\u0432\u0430 \u043f\u043e\u0441\u043b\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u0438\")\n            return False\n            \n        # \ud83d\udd25 \u0423\u0421\u0418\u041b\u0415\u041d\u041d\u042b\u0415 \u041a\u0420\u0418\u0422\u0415\u0420\u0418\u0418: \u0414\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0437\u043d\u0430\u0447\u0438\u043c\u043e\u0435 \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u0432\n        common_significant_words = words1_filtered & words2_filtered\n        print(f\"  \ud83d\udccb \u041e\u0431\u0449\u0438\u0435 \u0441\u043b\u043e\u0432\u0430: {common_significant_words}\")\n        \n        if not common_significant_words:\n            print(f\"  \u274c \u041e\u0422\u041a\u041b\u041e\u041d\u0415\u041d\u041e: \u041d\u0435\u0442 \u043e\u0431\u0449\u0438\u0445 \u0437\u043d\u0430\u0447\u0438\u043c\u044b\u0445 \u0441\u043b\u043e\u0432\")\n            return False\n        \n        # \ud83d\udd25 \u041d\u041e\u0412\u042b\u0419 \u041a\u0420\u0418\u0422\u0415\u0420\u0418\u0419: \u041e\u0442\u043a\u043b\u043e\u043d\u044f\u0435\u043c \u0435\u0441\u043b\u0438 \u043e\u0431\u0449\u0438\u0435 \u0441\u043b\u043e\u0432\u0430 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043e\u0431\u0449\u0438\u0435\n        # \u0421\u043b\u043e\u0432\u0430 \u0442\u0438\u043f\u0430 \"stream\", \"dual\", \"output\", \"input\" \u043c\u043e\u0433\u0443\u0442 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044c\u0441\u044f \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430\u0445\n        very_common_tech_words = {'stream', 'output', 'input', 'mode', 'type', 'format', 'level', 'rate', 'dual', 'multi'}\n        only_common_tech_words = common_significant_words.issubset(very_common_tech_words)\n        \n        if only_common_tech_words and len(common_significant_words) == 1:\n            common_word = list(common_significant_words)[0]\n            print(f\"  \u274c \u041e\u0422\u041a\u041b\u041e\u041d\u0415\u041d\u041e: \u0422\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043b\u043e\u0432\u043e '{common_word}' \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e\")\n            return False\n        \n        # \ud83d\udd25 \u0414\u041e\u041f\u041e\u041b\u041d\u0418\u0422\u0415\u041b\u042c\u041d\u0410\u042f \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410: \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043e\u0431\u0449\u0438\u0435 \u0441\u043b\u043e\u0432\u0430, \u043d\u043e \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0440\u0430\u0437\u043d\u044b\u0439\n        if len(common_significant_words) == 1:\n            # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0435 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043b\u0438 \u0440\u0430\u0437\u043d\u044b\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043b\u043e\u0432\u0430\n            unique_words1 = words1_filtered - common_significant_words\n            unique_words2 = words2_filtered - common_significant_words\n            \n            if unique_words1 and unique_words2:\n                # \u0415\u0441\u0442\u044c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043b\u043e\u0432\u0430 \u0432 \u043e\u0431\u0435\u0438\u0445 \u0433\u0440\u0443\u043f\u043f\u0430\u0445 - \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\n                print(f\"  \ud83d\udccb \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043b\u043e\u0432\u04301: {unique_words1}\")\n                print(f\"  \ud83d\udccb \u0423\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043b\u043e\u0432\u04302: {unique_words2}\")\n                \n                # \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u044c\n                incompatible_pairs = [\n                    ({'multi'}, {'adc', 'dac', 'analog', 'digital'}),\n                    ({'dual'}, {'ir', 'infrared', 'night'}),\n                    ({'audio'}, {'video', 'image', 'picture'}),\n                    ({'ip'}, {'stream', 'streaming', 'video', 'audio'}),  # \ud83c\udd95 IP \u2260 Stream!\n                    ({'stream'}, {'ip', 'protocol', 'network', 'address'}),  # \ud83c\udd95 Stream \u2260 IP!\n                ]\n                \n                for set1, set2 in incompatible_pairs:\n                    if (unique_words1 & set1 and unique_words2 & set2) or (unique_words1 & set2 and unique_words2 & set1):\n                        print(f\"  \u274c \u041e\u0422\u041a\u041b\u041e\u041d\u0415\u041d\u041e: \u0421\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u0435 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u044b\")\n                        return False\n        \n        # 3. \u0412\u042b\u0421\u041e\u041a\u0410\u042f \u0421\u0425\u041e\u0416\u0415\u0421\u0422\u042c \u041f\u041e \u041d\u0415\u0421\u041a\u041e\u041b\u042c\u041a\u0418\u041c \u041c\u0415\u0422\u0420\u0418\u041a\u0410\u041c \u041e\u0414\u041d\u041e\u0412\u0420\u0415\u041c\u0415\u041d\u041d\u041e\n        ratio = fuzz.ratio(param1_lower, param2_lower)\n        token_sort_ratio = fuzz.token_sort_ratio(param1_lower, param2_lower)\n        token_set_ratio = fuzz.token_set_ratio(param1_lower, param2_lower)\n        \n        print(f\"  \ud83d\udcca \u041c\u0435\u0442\u0440\u0438\u043a\u0438 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438:\")\n        print(f\"    \u2022 ratio: {ratio}\")\n        print(f\"    \u2022 token_sort_ratio: {token_sort_ratio}\")\n        print(f\"    \u2022 token_set_ratio: {token_set_ratio}\")\n        \n        # \u0411\u041e\u041b\u0415\u0415 \u0421\u0422\u0420\u041e\u0413\u0418\u0415 \u041f\u041e\u0420\u041e\u0413\u0418\n        high_threshold = 85\n        \n        # \u0422\u0440\u0435\u0431\u0443\u0435\u043c \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438 \u043f\u043e \u041d\u0415\u0421\u041a\u041e\u041b\u042c\u041a\u0418\u041c \u043c\u0435\u0442\u0440\u0438\u043a\u0430\u043c\n        high_similarity_count = 0\n        if ratio >= high_threshold:\n            high_similarity_count += 1\n            print(f\"    \u2705 ratio >= {high_threshold}\")\n        if token_sort_ratio >= high_threshold:\n            high_similarity_count += 1\n            print(f\"    \u2705 token_sort_ratio >= {high_threshold}\")\n        if token_set_ratio >= high_threshold:\n            high_similarity_count += 1\n            print(f\"    \u2705 token_set_ratio >= {high_threshold}\")\n        \n        print(f\"  \ud83d\udcca \u0412\u044b\u0441\u043e\u043a\u0438\u0445 \u043c\u0435\u0442\u0440\u0438\u043a: {high_similarity_count}/3\")\n        \n        # \u0422\u0440\u0435\u0431\u0443\u0435\u043c \u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u0438\u0437 3 \u043c\u0435\u0442\u0440\u0438\u043a \u0441 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u044c\u044e\n        if high_similarity_count >= 2:\n            print(f\"  \u2705 \u041f\u0420\u0418\u041d\u042f\u0422\u041e: \u0412\u044b\u0441\u043e\u043a\u0430\u044f \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u044c \u043f\u043e {high_similarity_count} \u043c\u0435\u0442\u0440\u0438\u043a\u0430\u043c\")\n            return True\n        \n        # 4. \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u041d\u0410 \u041f\u0420\u041e\u0421\u0422\u042b\u0415 \u0412\u0410\u0420\u0418\u0410\u0426\u0418\u0418 (\u0434\u0435\u0444\u0438\u0441\u044b, \u043f\u0440\u043e\u0431\u0435\u043b\u044b, \u0440\u0435\u0433\u0438\u0441\u0442\u0440)\n        normalized1 = re.sub(r'[-_\\s]+', '', param1_lower)\n        normalized2 = re.sub(r'[-_\\s]+', '', param2_lower)\n        \n        print(f\"  \ud83d\udccb \u041d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435: '{normalized1}' \u2194 '{normalized2}'\")\n        \n        if normalized1 == normalized2:\n            print(f\"  \u2705 \u041f\u0420\u0418\u041d\u042f\u0422\u041e: \u041e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0435 \u043f\u043e\u0441\u043b\u0435 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\")\n            return True\n        \n        # 5. \u041f\u0420\u041e\u0412\u0415\u0420\u041a\u0410 \u041d\u0410 \u041e\u0427\u0415\u041d\u042c \u041f\u041e\u0425\u041e\u0416\u0418\u0415 \u041e\u041a\u041e\u041d\u0427\u0410\u041d\u0418\u042f/\u041d\u0410\u0427\u0410\u041b\u0410\n        if len(common_significant_words) > 0:\n            print(f\"  \ud83d\udd0d \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u0439...\")\n            for word1 in words1_filtered:\n                for word2 in words2_filtered:\n                    if (len(word1) >= 4 and len(word2) >= 4 and\n                        (word1.startswith(word2[:4]) or word2.startswith(word1[:4])) and\n                        abs(len(word1) - len(word2)) <= 3):\n                        edit_ratio = fuzz.ratio(word1, word2)\n                        print(f\"    \ud83d\udccb \u0421\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u0435 '{word1}' \u2194 '{word2}': {edit_ratio}\")\n                        if edit_ratio >= 80:\n                            print(f\"    \u2705 \u041f\u0420\u0418\u041d\u042f\u0422\u041e: \u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f ({edit_ratio}%)\")\n                            return True\n        \n        print(f\"  \u274c \u041e\u0422\u041a\u041b\u041e\u041d\u0415\u041d\u041e: \u041d\u0435 \u043f\u0440\u043e\u0448\u043b\u0438 \u0432\u0441\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438\")\n        return False\n    \n    def calculate_edit_distance(self, s1, s2):\n        \"\"\"\u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u041b\u0435\u0432\u0435\u043d\u0448\u0442\u0435\u0439\u043d\u0430 \u043c\u0435\u0436\u0434\u0443 \u0434\u0432\u0443\u043c\u044f \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438\"\"\"\n        if len(s1) < len(s2):\n            return self.calculate_edit_distance(s2, s1)\n        \n        if len(s2) == 0:\n            return len(s1)\n        \n        previous_row = list(range(len(s2) + 1))\n        for i, c1 in enumerate(s1):\n            current_row = [i + 1]\n            for j, c2 in enumerate(s2):\n                insertions = previous_row[j + 1] + 1\n                deletions = current_row[j] + 1\n                substitutions = previous_row[j] + (c1 != c2)\n                current_row.append(min(insertions, deletions, substitutions))\n            previous_row = current_row\n        \n        return previous_row[-1]\n    \n    def check_typo_similarity(self, s1, s2):\n        \"\"\"\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043b\u0438 \u0441\u0442\u0440\u043e\u043a\u0438 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\u043c\u0438 \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0430\"\"\"\n        len_diff = abs(len(s1) - len(s2))\n        max_len = max(len(s1), len(s2))\n        min_len = min(len(s1), len(s2))\n        \n        # \u0421\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0432 \u0434\u043b\u0438\u043d\u0435 - \u043d\u0435 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\n        if len_diff > 2:\n            return False\n        \n        # \u0415\u0441\u043b\u0438 \u043e\u0434\u043d\u043e \u0441\u043b\u043e\u0432\u043e \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u0438\u043d\u043d\u0435\u0435 \u0434\u0440\u0443\u0433\u043e\u0433\u043e (\u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u043d\u0430 30%), \u044d\u0442\u043e \u0441\u043a\u043e\u0440\u0435\u0435 \u043d\u0435 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\n        if max_len > 0 and len_diff / max_len > 0.4:\n            return False\n            \n        distance = self.calculate_edit_distance(s1, s2)\n        \n        # \u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0434\u043b\u044f \u043e\u0447\u0435\u043d\u044c \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0441\u043b\u043e\u0432 (2-3 \u0441\u0438\u043c\u0432\u043e\u043b\u0430)\n        if max_len <= 3:\n            return distance == 1  # \u0422\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0430 \u043e\u0448\u0438\u0431\u043a\u0430\n        \n        # \u0414\u043b\u044f \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0445 \u0441\u043b\u043e\u0432 (4-6 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432)\n        if max_len <= 6:\n            # \u0415\u0441\u043b\u0438 \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0432 \u0434\u043b\u0438\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u0435 1 \u0441\u0438\u043c\u0432\u043e\u043b\u0430, \u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 1\n            if len_diff > 1:\n                return distance == 1\n            else:\n                return distance <= 2\n        \n        # \u0414\u043b\u044f \u0441\u0440\u0435\u0434\u043d\u0438\u0445 \u0441\u043b\u043e\u0432 (7-10 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432)\n        if max_len <= 10:\n            return distance <= 2\n        \n        # \u0414\u043b\u044f \u0434\u043b\u0438\u043d\u043d\u044b\u0445 \u0441\u043b\u043e\u0432\n        return distance <= 3\n\n    def find_similar_values_in_all_parameters(self):\n        \"\"\"\u041f\u043e\u0438\u0441\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445\"\"\"\n        similar_values_data = {}\n        \n        # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0441\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\n        param_values = defaultdict(set)\n        for sku_params in self.original_values.values():\n            for param, values_list in sku_params.items():\n                if isinstance(values_list, list):\n                    param_values[param].update(values_list)\n                else:\n                    param_values[param].add(values_list)\n        \n        # \u0418\u0449\u0435\u043c \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0435\n        for param, values in param_values.items():\n            values_list = list(values)\n            if len(values_list) < 2:\n                continue\n                \n            similar_groups = {}\n            processed = set()\n            \n            for i, val1 in enumerate(values_list):\n                if val1 in processed:\n                    continue\n                    \n                group = [val1]\n                for j, val2 in enumerate(values_list[i+1:], i+1):\n                    if val2 in processed:\n                        continue\n                        \n                    # \u0420\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u043c\u0435\u0442\u0440\u0438\u043a\u0438 \u0441\u0445\u043e\u0436\u0435\u0441\u0442\u0438\n                    ratio = fuzz.ratio(val1.lower(), val2.lower())\n                    token_sort_ratio = fuzz.token_sort_ratio(val1.lower(), val2.lower())\n                    \n                    if ratio >= 80 or token_sort_ratio >= 85:\n                        group.append(val2)\n                        processed.add(val2)\n                \n                if len(group) > 1:\n                    representative = min(group, key=len)  # \u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u0430\u043c\u043e\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0435 \u043a\u0430\u043a \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043b\u044f\n                    similar_groups[representative] = group\n                    processed.update(group)\n            \n            if similar_groups:\n                similar_values_data[param] = similar_groups\n        \n        return similar_values_data\n\n    # \ud83c\udd95 \u0424\u0423\u041d\u041a\u0426\u0418\u0418 \u0414\u041b\u042f \u0420\u0415\u0414\u0410\u041a\u0422\u0418\u0420\u041e\u0412\u0410\u041d\u0418\u042f \u0413\u0420\u0423\u041f\u041f \u041f\u0410\u0420\u0410\u041c\u0415\u0422\u0420\u041e\u0412\n\n    def show_params_context_menu(self, event):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u0434\u043b\u044f \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        try:\n            # \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c, \u043d\u0430 \u043a\u0430\u043a\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0435 \u0431\u044b\u043b \u043a\u043b\u0438\u043a\n            item = self.params_tree.identify_row(event.y)\n            if item:\n                self.params_tree.selection_set(item)\n                self.params_context_menu.post(event.x_root, event.y_root)\n        except:\n            pass\n\n    def edit_param_group(self):\n        \"\"\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        try:\n            selection = self.params_tree.selection()\n            if not selection:\n                messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u0440\u0443\u043f\u043f\u0443 \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.\")\n                return\n            \n            item = selection[0]\n            values = self.params_tree.item(item, \"values\")\n            representative = values[1]  # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            current_similar = values[2]  # \u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n            \n            if representative not in self.param_merge_settings:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0413\u0440\u0443\u043f\u043f\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445.\")\n                return\n            \n            # \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\n            self.open_group_edit_dialog(representative, current_similar)\n            \n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0433\u0440\u0443\u043f\u043f\u044b: {e}\")\n\n    def remove_from_param_group(self):\n        \"\"\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b\"\"\"\n        try:\n            selection = self.params_tree.selection()\n            if not selection:\n                messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u0440\u0443\u043f\u043f\u0443 \u0434\u043b\u044f \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.\")\n                return\n            \n            item = selection[0]\n            values = self.params_tree.item(item, \"values\")\n            representative = values[1]  # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            current_similar = values[2]  # \u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n            \n            if representative not in self.param_merge_settings:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0413\u0440\u0443\u043f\u043f\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445.\")\n                return\n            \n            # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n            similar_list = self.param_merge_settings[representative].get('similar_params', [])\n            \n            if not similar_list:\n                messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u0412 \u0433\u0440\u0443\u043f\u043f\u0435 \u043d\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f.\")\n                return\n            \n            # \u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\n            self.open_remove_params_dialog(representative, similar_list)\n            \n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b: {e}\")\n\n    def delete_param_group(self):\n        \"\"\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u044e \u0433\u0440\u0443\u043f\u043f\u0443 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\"\"\"\n        try:\n            selection = self.params_tree.selection()\n            if not selection:\n                messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0433\u0440\u0443\u043f\u043f\u0443 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f.\")\n                return\n            \n            item = selection[0]\n            values = self.params_tree.item(item, \"values\")\n            representative = values[1]  # \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\n            \n            if representative not in self.param_merge_settings:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", \"\u0413\u0440\u0443\u043f\u043f\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445.\")\n                return\n            \n            # \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\n            result = messagebox.askyesno(\n                \"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \n                f\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0433\u0440\u0443\u043f\u043f\u0443 '{representative}' \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e?\\n\\n\u042d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043d\u0435\u043b\u044c\u0437\u044f \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c.\"\n            )\n            \n            if result:\n                del self.param_merge_settings[representative]\n                self.save_merge_settings()\n                self.update_params_tree()\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", f\"\u0413\u0440\u0443\u043f\u043f\u0430 '{representative}' \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\")\n            \n        except Exception as e:\n            messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438 \u0433\u0440\u0443\u043f\u043f\u044b: {e}\")\n\n    def open_group_edit_dialog(self, representative, current_similar):\n        \"\"\"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0433\u0440\u0443\u043f\u043f\u044b\"\"\"\n        dialog = Toplevel(self.root)\n        dialog.title(f\"\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b: {representative}\")\n        dialog.geometry(\"600x400\")\n        dialog.transient(self.root)\n        dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        dialog.update_idletasks()\n        x = (dialog.winfo_screenwidth() // 2) - (dialog.winfo_width() // 2)\n        y = (dialog.winfo_screenheight() // 2) - (dialog.winfo_height() // 2)\n        dialog.geometry(f\"+{x}+{y}\")\n        \n        # \u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a\n        Label(dialog, text=f\"\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440: {representative}\", \n              font=(\"Arial\", 12, \"bold\")).pack(pady=10)\n        \n        # \u0422\u0435\u043a\u0443\u0449\u0438\u0435 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n        Label(dialog, text=\"\u041f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b (\u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0443):\").pack(anchor=W, padx=10)\n        \n        text_frame = Frame(dialog)\n        text_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        text_widget = Text(text_frame, height=15, width=70)\n        scrollbar = Scrollbar(text_frame, orient=VERTICAL, command=text_widget.yview)\n        text_widget.configure(yscrollcommand=scrollbar.set)\n        \n        text_widget.pack(side=LEFT, fill=BOTH, expand=True)\n        scrollbar.pack(side=RIGHT, fill=Y)\n        \n        # \u0417\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438\n        current_params = self.param_merge_settings[representative].get('similar_params', [])\n        text_widget.insert(END, '\\n'.join(current_params))\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        button_frame = Frame(dialog)\n        button_frame.pack(fill=X, padx=10, pady=10)\n        \n        def save_changes():\n            try:\n                # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n                content = text_widget.get(1.0, END).strip()\n                new_params = [p.strip() for p in content.split('\\n') if p.strip()]\n                \n                # \u0423\u0434\u0430\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440, \u0435\u0441\u043b\u0438 \u043e\u043d \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e \u043f\u043e\u043f\u0430\u043b \u0432 \u0441\u043f\u0438\u0441\u043e\u043a\n                new_params = [p for p in new_params if p != representative]\n                \n                if not new_params:\n                    # \u0415\u0441\u043b\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0443\u0441\u0442, \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0432\u0441\u044e \u0433\u0440\u0443\u043f\u043f\u0443\n                    if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \n                                         \"\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043f\u0443\u0441\u0442.\\n\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u044e \u0433\u0440\u0443\u043f\u043f\u0443?\"):\n                        del self.param_merge_settings[representative]\n                        self.save_merge_settings()\n                        self.update_params_tree()\n                        dialog.destroy()\n                        messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", \"\u0413\u0440\u0443\u043f\u043f\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0430.\")\n                    return\n                \n                # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\n                self.param_merge_settings[representative]['similar_params'] = new_params\n                self.save_merge_settings()\n                self.update_params_tree()\n                dialog.destroy()\n                messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", \"\u0413\u0440\u0443\u043f\u043f\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430.\")\n                \n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438: {e}\")\n        \n        def cancel_changes():\n            dialog.destroy()\n        \n        Button(button_frame, text=\"\ud83d\udcbe \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=save_changes).pack(side=LEFT, padx=5)\n        Button(button_frame, text=\"\u274c \u041e\u0442\u043c\u0435\u043d\u0430\", command=cancel_changes).pack(side=LEFT, padx=5)\n        \n        # \u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0430\n        Label(dialog, text=\"\ud83d\udca1 \u0421\u043e\u0432\u0435\u0442: \u0423\u0434\u0430\u043b\u0438\u0442\u0435 \u043d\u0435\u043d\u0443\u0436\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u043d\u043e\u0432\u044b\u0435\", \n              font=(\"Arial\", 9), fg=\"gray\").pack(pady=5)\n\n    def open_remove_params_dialog(self, representative, similar_list):\n        \"\"\"\u041e\u0442\u043a\u0440\u044b\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433 \u0432\u044b\u0431\u043e\u0440\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\"\"\"\n        dialog = Toplevel(self.root)\n        dialog.title(f\"\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b: {representative}\")\n        dialog.geometry(\"500x300\")\n        dialog.transient(self.root)\n        dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        dialog.update_idletasks()\n        x = (dialog.winfo_screenwidth() // 2) - (dialog.winfo_width() // 2)\n        y = (dialog.winfo_screenheight() // 2) - (dialog.winfo_height() // 2)\n        dialog.geometry(f\"+{x}+{y}\")\n        \n        Label(dialog, text=f\"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b '{representative}':\", \n              font=(\"Arial\", 11, \"bold\")).pack(pady=10)\n        \n        # \u0421\u043f\u0438\u0441\u043e\u043a \u0441 \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u0430\u043c\u0438\n        frame = Frame(dialog)\n        frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        canvas = Canvas(frame)\n        scrollbar = Scrollbar(frame, orient=\"vertical\", command=canvas.yview)\n        scrollable_frame = Frame(canvas)\n        \n        scrollable_frame.bind(\n            \"<Configure>\",\n            lambda e: canvas.configure(scrollregion=canvas.bbox(\"all\"))\n        )\n        \n        canvas.create_window((0, 0), window=scrollable_frame, anchor=\"nw\")\n        canvas.configure(yscrollcommand=scrollbar.set)\n        \n        canvas.pack(side=\"left\", fill=\"both\", expand=True)\n        scrollbar.pack(side=\"right\", fill=\"y\")\n        \n        # \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0447\u0435\u043a\u0431\u043e\u043a\u0441\u043e\u0432\n        checkbox_vars = {}\n        for param in similar_list:\n            var = BooleanVar()\n            checkbox_vars[param] = var\n            Checkbutton(scrollable_frame, text=param, variable=var).pack(anchor=W, padx=5, pady=2)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        button_frame = Frame(dialog)\n        button_frame.pack(fill=X, padx=10, pady=10)\n        \n        def remove_selected():\n            try:\n                # \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\n                to_remove = [param for param, var in checkbox_vars.items() if var.get()]\n                \n                if not to_remove:\n                    messagebox.showwarning(\"\u041f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0436\u0434\u0435\u043d\u0438\u0435\", \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f.\")\n                    return\n                \n                # \u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\n                result = messagebox.askyesno(\n                    \"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \n                    f\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c {len(to_remove)} \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440(\u043e\u0432) \u0438\u0437 \u0433\u0440\u0443\u043f\u043f\u044b?\\n\\n\" + \n                    '\\n'.join(to_remove)\n                )\n                \n                if result:\n                    # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a\n                    new_similar_list = [p for p in similar_list if p not in to_remove]\n                    \n                    if not new_similar_list:\n                        # \u0415\u0441\u043b\u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0442\u0430\u043b \u043f\u0443\u0441\u0442\u044b\u043c, \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0432\u0441\u044e \u0433\u0440\u0443\u043f\u043f\u0443\n                        if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \n                                             \"\u0421\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u0445\u043e\u0436\u0438\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u0443\u0441\u0442\u044b\u043c.\\n\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u044e \u0433\u0440\u0443\u043f\u043f\u0443?\"):\n                            del self.param_merge_settings[representative]\n                        dialog.destroy()\n                    else:\n                        # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a\n                        self.param_merge_settings[representative]['similar_params'] = new_similar_list\n                        dialog.destroy()\n                    \n                    self.save_merge_settings()\n                    self.update_params_tree()\n                    messagebox.showinfo(\"\u0423\u0441\u043f\u0435\u0445\", f\"\u0423\u0434\u0430\u043b\u0435\u043d\u043e {len(to_remove)} \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440(\u043e\u0432).\")\n                \n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430\", f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043f\u0440\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0438: {e}\")\n        \n        def cancel_removal():\n            dialog.destroy()\n        \n        Button(button_frame, text=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435\", command=remove_selected).pack(side=LEFT, padx=5)\n        Button(button_frame, text=\"\u274c \u041e\u0442\u043c\u0435\u043d\u0430\", command=cancel_removal).pack(side=LEFT, padx=5)\n\n    def open_exclusions_dialog(self):\n        \"\"\"\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0434\u0438\u0430\u043b\u043e\u0433 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\u043c\u0438\"\"\"\n        dialog = Toplevel(self.root)\n        dialog.title(\"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\u043c\u0438\")\n        dialog.geometry(\"500x400\")\n        dialog.transient(self.root)\n        dialog.grab_set()\n        \n        # \u0426\u0435\u043d\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u0434\u0438\u0430\u043b\u043e\u0433\n        dialog.geometry(\"+%d+%d\" % (self.root.winfo_rootx() + 100, self.root.winfo_rooty() + 100))\n        \n        Label(dialog, text=\"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\u043c\u0438\", font=(\"Arial\", 12, \"bold\")).pack(pady=10)\n        Label(dialog, text=\"\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u044f\u0442\u044c\u0441\u044f \u043d\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", \n              font=(\"Arial\", 9)).pack(pady=(0, 10))\n        \n        # \u0424\u0440\u0435\u0439\u043c \u0441\u043e \u0441\u043f\u0438\u0441\u043a\u043e\u043c\n        list_frame = Frame(dialog)\n        list_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)\n        \n        # \u0421\u043f\u0438\u0441\u043e\u043a \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439\n        self.exclusions_listbox = Listbox(list_frame)\n        self.exclusions_listbox.pack(side=LEFT, fill=BOTH, expand=True)\n        \n        scrollbar = Scrollbar(list_frame, orient=VERTICAL, command=self.exclusions_listbox.yview)\n        scrollbar.pack(side=RIGHT, fill=Y)\n        self.exclusions_listbox.configure(yscrollcommand=scrollbar.set)\n        \n        # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a\n        for exclusion in self.exclusions:\n            self.exclusions_listbox.insert(END, exclusion)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n        btn_frame = Frame(dialog)\n        btn_frame.pack(fill=X, padx=10, pady=10)\n        \n        Button(btn_frame, text=\"\u2795 \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\", command=lambda: self.add_exclusion_dialog(dialog)).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\ud83d\uddd1\ufe0f \u0423\u0434\u0430\u043b\u0438\u0442\u044c\", command=lambda: self.delete_exclusion_dialog(dialog)).pack(side=LEFT, padx=5)\n        Button(btn_frame, text=\"\u2705 \u0417\u0430\u043a\u0440\u044b\u0442\u044c\", command=dialog.destroy).pack(side=RIGHT, padx=5)\n\n    def add_exclusion_dialog(self, parent):\n        \"\"\"\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\"\"\"\n        new_exclusion = simpledialog.askstring(\"\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\", \n                                              \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0434\u043b\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f:\", \n                                              parent=parent)\n        if new_exclusion and new_exclusion.strip():\n            new_exclusion = new_exclusion.strip()\n            if new_exclusion not in self.exclusions:\n                self.exclusions.append(new_exclusion)\n                self.exclusions_listbox.insert(END, new_exclusion)\n                self.save_excl()\n\n    def delete_exclusion_dialog(self, parent):\n        \"\"\"\u0423\u0434\u0430\u043b\u044f\u0435\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\"\"\"\n        selection = self.exclusions_listbox.curselection()\n        if selection:\n            index = selection[0]\n            exclusion = self.exclusions_listbox.get(index)\n            if messagebox.askyesno(\"\u041f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\", \n                                 f\"\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 '{exclusion}'?\", \n                                 parent=parent):\n                self.exclusions.remove(exclusion)\n                self.exclusions_listbox.delete(index)\n                self.save_excl()\n\n    def show_about_dialog(self):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435\"\"\"\n        translation_status = \"\u2705 \u0410\u043a\u0442\u0438\u0432\u043d\u0430\" if self.translation_available else \"\u274c \u041d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430\"\n        \n        about_text = f\"\"\"\u0420\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 v4.5 + \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\n\n\ud83c\udfaf \u041a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0430\u044f \u044d\u0440\u0433\u043e\u043d\u043e\u043c\u0438\u0447\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f\n\ud83c\udf10 \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430: {translation_status}\n\n\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438:\n\u2022 \u041f\u0430\u0440\u0441\u0438\u043d\u0433 HTML-\u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a \u0438\u0437 Excel\n\u2022 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\u2022 \u041c\u0430\u043f\u043f\u0438\u043d\u0433 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n\u2022 \u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d \u0438 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\n\u2022 \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\u2022 \u0417\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\n\u2022 \ud83c\udf10 \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 EN\u2192RU\n\u2022 \ud83c\udfaf \u0418\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u2022 \ud83d\udcda \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u2022 \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432\n\n\u0412\u0435\u0440\u0441\u0438\u044f: 4.5 + Translation System\n\u0414\u0430\u0442\u0430: 2025-06-02\n\u0410\u0432\u0442\u043e\u0440: AI Assistant\n\n\u00a9 2025 - \u0418\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0442\u043e\u0432\u0430\u0440\u043d\u044b\u0445 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432\"\"\"\n        \n        messagebox.showinfo(\"\u041e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435\", about_text)\n\n    # \ud83c\udf10 \u041c\u0415\u0422\u041e\u0414\u042b \u0414\u041b\u042f \u0420\u0410\u0411\u041e\u0422\u042b \u0421 \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u0410\u041c\u0418\n\n    def open_translation_dialog(self):\n        \"\"\"\u041e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n        if not self.translation_available:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \n                                 \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432.\")\n            return\n        \n        # \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043c\u0435\u043d\u044e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n        menu = Menu(self.root, tearoff=0)\n        \n        # \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n        menu.add_command(label=\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\", \n                        command=self.translate_current_parameters)\n        menu.add_command(label=\"\ud83d\udd24 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\", \n                        command=self.translate_current_values)\n        menu.add_separator()\n        \n        # \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\u043c\u0438\n        menu.add_command(label=\"\ud83d\udce5 \u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", \n                        command=self.import_translations)\n        menu.add_command(label=\"\ud83d\udce4 \u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", \n                        command=self.export_translations)\n        menu.add_separator()\n        \n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n        menu.add_command(label=\"\u2699\ufe0f \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", \n                        command=self.show_translation_settings)\n        menu.add_command(label=\"\u2139\ufe0f \u041e \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", \n                        command=self.show_translation_info)\n        \n        # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043c\u0435\u043d\u044e \u0440\u044f\u0434\u043e\u043c \u0441 \u043a\u043d\u043e\u043f\u043a\u043e\u0439\n        try:\n            x = self.btn_translate.winfo_rootx()\n            y = self.btn_translate.winfo_rooty() + self.btn_translate.winfo_height()\n            menu.post(x, y)\n        except Exception as e:\n            print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043c\u0435\u043d\u044e: {e}\")\n            # Fallback - \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0432 \u0446\u0435\u043d\u0442\u0440\u0435 \u043e\u043a\u043d\u0430\n            menu.post(self.root.winfo_x() + 100, self.root.winfo_y() + 100)\n\n    def show_translation_settings(self):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n        if not self.translation_available:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.\")\n            return\n        \n        # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0442\u0438\u043f \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n        if TRANSLATION_SYSTEM_TYPE in [\"advanced\", \"basic_integrated\"]:\n            try:\n                # \u041f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\n                dialog = TranslationManagementDialog(self.root, self.translation_system)\n                return\n            except Exception as e:\n                print(f\"\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0442\u043a\u0440\u044b\u0442\u0438\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u0434\u0438\u0430\u043b\u043e\u0433\u0430: {e}\")\n        \n        # \u0414\u043b\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0438\u043b\u0438 \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435 - \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n        self.show_simple_translation_settings()\n\n    def show_simple_translation_settings(self):\n        \"\"\"\u041f\u0440\u043e\u0441\u0442\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n        settings_window = Toplevel(self.root)\n        settings_window.title(\"\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\")\n        settings_window.geometry(\"400x300\")\n        settings_window.transient(self.root)\n        settings_window.grab_set()\n        \n        # \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n        auto_frame = LabelFrame(settings_window, text=\"\u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434\", padx=10, pady=10)\n        auto_frame.pack(fill=X, padx=10, pady=10)\n        \n        auto_var = BooleanVar()\n        if hasattr(self.translation_system, 'auto_translate'):\n            auto_var.set(self.translation_system.auto_translate)\n        else:\n            auto_var.set(True)\n            \n        auto_check = Checkbutton(auto_frame, text=\"\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434\", variable=auto_var)\n        auto_check.pack(anchor=W)\n        \n        # \u041a\u043d\u043e\u043f\u043a\u0438\n        btn_frame = Frame(settings_window)\n        btn_frame.pack(fill=X, padx=10, pady=10)\n        \n        def save_settings():\n            if hasattr(self.translation_system, 'auto_translate'):\n                self.translation_system.auto_translate = auto_var.get()\n            settings_window.destroy()\n            # \u041e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0441 \u043d\u043e\u0432\u044b\u043c\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438\n            self.update_main_tree_with_mappings()\n        \n        Button(btn_frame, text=\"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c\", command=save_settings).pack(side=RIGHT, padx=5)\n        Button(btn_frame, text=\"\u041e\u0442\u043c\u0435\u043d\u0430\", command=settings_window.destroy).pack(side=RIGHT)\n\n    def show_translation_info(self):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n        if not self.translation_available:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.\")\n            return\n        \n        # \u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0435\n        info_lines = [\n            \"\ud83c\udf10 \u0421\u0418\u0421\u0422\u0415\u041c\u0410 \u041f\u0415\u0420\u0415\u0412\u041e\u0414\u041e\u0412\",\n            \"\",\n            f\"\ud83d\udcda \u0414\u043e\u0441\u0442\u0443\u043f\u043d\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432: {len(getattr(self.translation_system, 'translation_dict', {}))}\"\n        ]\n        \n        if hasattr(self.translation_system, 'auto_translate'):\n            status = \"\u0412\u043a\u043b\u044e\u0447\u0435\u043d\" if self.translation_system.auto_translate else \"\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\"\n            info_lines.append(f\"\ud83c\udfaf \u0410\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434: {status}\")\n        \n        if hasattr(self.translation_system, 'fuzzy_match_threshold'):\n            info_lines.append(f\"\ud83d\udd0d \u041d\u0435\u0447\u0435\u0442\u043a\u0438\u0439 \u043f\u043e\u0438\u0441\u043a: {self.translation_system.fuzzy_match_threshold}%\")\n        \n        info_lines.extend([\n            \"\",\n            \"\ud83d\udccb \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439:\",\n            f\"\u2022 \u0412\u0441\u0435\u0433\u043e \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439: {len(self.mappings)}\",\n            f\"\u2022 \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043e \u0441\u0442\u0440\u043e\u043a: {len(self.tree.get_children()) if hasattr(self, 'tree') else 0}\"\n        ])\n        \n        messagebox.showinfo(\"\u041e \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\", \"\\n\".join(info_lines))\n\n    def translate_current_parameters(self):\n        \"\"\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430\u0445\"\"\"\n        if not self.translation_available or not self.translation_system:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.\")\n            return\n        \n        if not self.mappings:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041d\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0443.\")\n            return\n        \n        translated_count = 0\n        for original_param in list(self.mappings.keys()):\n            translated = self.translation_system.translate_text(original_param, is_parameter=True)\n            if translated != original_param:\n                # \u0417\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043b\u044e\u0447 \u0432 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430\u0445\n                self.mappings[translated] = self.mappings.pop(original_param)\n                translated_count += 1\n        \n        if translated_count > 0:\n            self.refresh_mapping_table()\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n            self.update_main_tree_with_mappings()\n            messagebox.showinfo(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", f\"\u041f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432: {translated_count}\")\n        else:\n            messagebox.showinfo(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", \"\u041d\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e.\")\n\n    def translate_current_values(self):\n        \"\"\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u0442 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0432 \u043c\u0430\u043f\u043f\u0438\u043d\u0433\u0430\u0445\"\"\"\n        if not self.translation_available or not self.translation_system:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.\")\n            return\n        \n        if not self.mappings:\n            messagebox.showinfo(\"\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f\", \"\u041d\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0443.\")\n            return\n        \n        translated_count = 0\n        for param, values in self.mappings.items():\n            if isinstance(values, list):\n                new_values = []\n                for value in values:\n                    translated = self.translation_system.translate_text(str(value), is_parameter=False)\n                    new_values.append(translated)\n                    if translated != str(value):\n                        translated_count += 1\n                self.mappings[param] = new_values\n        \n        if translated_count > 0:\n            self.refresh_mapping_table()\n            # \ud83d\udd27 \u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e: \u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u0443\u044e \u0442\u0430\u0431\u043b\u0438\u0446\u0443\n            self.update_main_tree_with_mappings()\n            messagebox.showinfo(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", f\"\u041f\u0435\u0440\u0435\u0432\u0435\u0434\u0435\u043d\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439: {translated_count}\")\n        else:\n            messagebox.showinfo(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", \"\u041d\u043e\u0432\u044b\u0445 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e.\")\n\n    def export_translations(self):\n        \"\"\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\"\"\"\n        if not self.translation_available or not self.translation_system:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.\")\n            return\n        \n        filename = filedialog.asksaveasfilename(\n            title=\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\",\n            defaultextension=\".json\",\n            filetypes=[(\"JSON files\", \"*.json\"), (\"All files\", \"*.*\")]\n        )\n        \n        if filename:\n            try:\n                export_data = {\n                    \"custom_translations\": getattr(self.translation_system, 'custom_translations', {}),\n                    \"settings\": {\n                        \"auto_translate\": getattr(self.translation_system, 'auto_translate', True),\n                        \"fuzzy_match_threshold\": getattr(self.translation_system, 'fuzzy_match_threshold', 80),\n                        \"translate_values\": getattr(self.translation_system, 'translate_values', True),\n                        \"translate_parameters\": getattr(self.translation_system, 'translate_parameters', True)\n                    }\n                }\n                \n                with open(filename, 'w', encoding='utf-8') as f:\n                    json.dump(export_data, f, ensure_ascii=False, indent=2)\n                \n                messagebox.showinfo(\"\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", f\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u044b \u0432 {filename}\")\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b: {e}\")\n\n    def import_translations(self):\n        \"\"\"\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\"\"\"\n        if not self.translation_available or not self.translation_system:\n            messagebox.showwarning(\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u043d\u0435\u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\", \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043d\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430.\")\n            return\n        \n        filename = filedialog.askopenfilename(\n            title=\"\u0418\u043c\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\",\n            filetypes=[(\"JSON files\", \"*.json\"), (\"All files\", \"*.*\")]\n        )\n        \n        if filename:\n            try:\n                with open(filename, 'r', encoding='utf-8') as f:\n                    import_data = json.load(f)\n                \n                # \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\n                if \"custom_translations\" in import_data:\n                    if hasattr(self.translation_system, 'custom_translations'):\n                        self.translation_system.custom_translations.update(import_data[\"custom_translations\"])\n                        if hasattr(self.translation_system, 'save_custom_translations'):\n                            self.translation_system.save_custom_translations()\n                \n                # \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438\n                if \"settings\" in import_data:\n                    settings = import_data[\"settings\"]\n                    if hasattr(self.translation_system, 'auto_translate'):\n                        self.translation_system.auto_translate = settings.get(\"auto_translate\", True)\n                    if hasattr(self.translation_system, 'fuzzy_match_threshold'):\n                        self.translation_system.fuzzy_match_threshold = settings.get(\"fuzzy_match_threshold\", 80)\n                    if hasattr(self.translation_system, 'translate_values'):\n                        self.translation_system.translate_values = settings.get(\"translate_values\", True)\n                    if hasattr(self.translation_system, 'translate_parameters'):\n                        self.translation_system.translate_parameters = settings.get(\"translate_parameters\", True)\n                \n                messagebox.showinfo(\"\u0418\u043c\u043f\u043e\u0440\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\", f\"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b \u0438\u0437 {filename}\")\n            except Exception as e:\n                messagebox.showerror(\"\u041e\u0448\u0438\u0431\u043a\u0430 \u0438\u043c\u043f\u043e\u0440\u0442\u0430\", f\"\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044b: {e}\")\n\n    def apply_auto_translation(self, text, is_parameter=True):\n        \"\"\"\u041f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043a \u0442\u0435\u043a\u0441\u0442\u0443, \u0435\u0441\u043b\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u043e\"\"\"\n        if not self.can_auto_translate():\n            return text\n            \n        # \u041f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0430\u0437\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0441\u0438\u0441\u0442\u0435\u043c\u044b\n        if hasattr(self.translation_system, 'translate_text'):\n            translated = self.translation_system.translate_text(text, is_parameter=is_parameter)\n            # \u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u043b\n            if translated != text:\n                print(f\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434: '{text}' \u2192 '{translated}'\")\n            return translated\n        elif hasattr(self.translation_system, 'basic_translate'):\n            translated = self.translation_system.basic_translate(text)\n            if translated != text:\n                print(f\"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434: '{text}' \u2192 '{translated}'\")\n            return translated\n            \n        return text\n\n    def can_auto_translate(self):\n        \"\"\"\u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u043b\u0438 \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\"\"\n        if not self.translation_available or not self.translation_system:\n            return False\n        \n        # \u0414\u043b\u044f \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0430\u0442\u0440\u0438\u0431\u0443\u0442 auto_translate\n        if hasattr(self.translation_system, 'auto_translate'):\n            return self.translation_system.auto_translate\n        \n        # \u0414\u043b\u044f IntegratedTranslationSystem \u0441\u0447\u0438\u0442\u0430\u0435\u043c, \u0447\u0442\u043e \u0430\u0432\u0442\u043e\u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d\n        return True\n\n    def show_hotkeys_dialog(self):\n        \"\"\"\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0433\u043e\u0440\u044f\u0447\u0438\u0445 \u043a\u043b\u0430\u0432\u0438\u0448\"\"\"\n        hotkeys_text = \"\"\"\u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438:\n\n\ud83d\udcc1 \u0424\u0430\u0439\u043b\u044b:\nCtrl+O - \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b\nCtrl+P - \u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u0430\u0431\u043b\u0438\u0446\u0443  \nCtrl+S - \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\nCtrl+Q - \u0412\u044b\u0445\u043e\u0434\n\n\u2328\ufe0f \u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435:\nCtrl+C - \u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c\nCtrl+V - \u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c\nCtrl+Z - \u041e\u0442\u043c\u0435\u043d\u0430\nCtrl+A - \u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435\nDelete - \u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435\n\n\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b:\n\u041c\u0435\u043d\u044e \"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\" \u2192 \"\u2699\ufe0f \u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\"\n\u041c\u0435\u043d\u044e \"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\" \u2192 \"\ud83d\udd04 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b\"\n\u041c\u0435\u043d\u044e \"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\" \u2192 \"\ud83d\udd04 \u041f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\"\n\u041a\u043d\u043e\u043f\u043a\u0430 \"\ud83c\udf10 \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432\n\n\ud83d\uddb1\ufe0f \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441:\n\u041f\u041a\u041c - \u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u043d\u043e\u0435 \u043c\u0435\u043d\u044e\nF1 - \u0421\u043f\u0440\u0430\u0432\u043a\u0430\nEsc - \u0417\u0430\u043a\u0440\u044b\u0442\u044c \u0434\u0438\u0430\u043b\u043e\u0433\"\"\"\n        \n        messagebox.showinfo(\"\u0413\u043e\u0440\u044f\u0447\u0438\u0435 \u043a\u043b\u0430\u0432\u0438\u0448\u0438\", hotkeys_text)\n\n\nif __name__ == \"__main__\":\n    root = Tk()\n    app = AttributeEditor(root)\n    root.mainloop()\n\n\u041d\u0430\u043f\u0438\u0448\u0438 \u043f\u043e \u043f\u0443\u043d\u043a\u0442\u0430\u043c, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u043c\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u0412\u043e\u0442 \u043f\u0440\u0438\u043c\u0435\u0440:\n\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\" \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \"\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\" \n\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\"\n\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \"\u041f\u0440\u0430\u0432\u0438\u043b\u0430 \u0437\u0430\u043c\u0435\u043d\"\n\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \"\u041d\u043e\u0432\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\" \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \"\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0439\"\n\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 (callbacks) \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u0438 \u043c\u0435\u0436\u0434\u0443 \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u043c\u0438\n\u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0435 \u043e\u0442\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0431\u0435\u0437 \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f\n\u0414\u0418\u041d\u0410\u041c\u0418\u0427\u0415\u0421\u041a\u041e\u0415 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n\u0417\u0410\u0429\u0418\u0422\u0410 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0445 \u0430\u0431\u0431\u0440\u0435\u0432\u0438\u0430\u0442\u0443\u0440 (H.264, G.711, IP67) \u043e\u0442 \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430  \n\u041c\u0410\u0421\u0421\u041e\u0412\u041e\u0415 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0422\u041e\u041b\u042c\u041a\u041e \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438 \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430\n\u0421\u0447\u0435\u0442\u0447\u0438\u043a \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438\n\u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d\u041e \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0432 \u0441\u043a\u043e\u0431\u043a\u0430\u0445 \"Auto (\u0410\u0422\u041e) / Color (\u0426\u0432.)\"\n\u0418\u0421\u041f\u0420\u0410\u0412\u041b\u0415\u041d \u0441\u0431\u043e\u0440 \u0412\u0421\u0415\u0425 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438\u0437 \u0412\u0421\u0415\u0425 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432\n\u0413\u0410\u0420\u0410\u041d\u0422\u0418\u0420\u041e\u0412\u0410\u041d\u041d\u041e\u0415 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0412\u0421\u0415\u0425 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\u041f\u0420\u0418\u041c\u0415\u041d\u0415\u041d\u0418\u0415 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u043a\u043e \u0432\u0441\u0435\u043c \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\n\u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0432 \u0441\u0442\u043e\u043b\u0431\u0446\u0435 \"\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u044f\"\n\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0438\u0437 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a\n\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0442\u0438\u043f\u0430 \"/;\"\n\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u044c\n\u041f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 GUI \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0432\u043c\u0435\u0441\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u043e\u043d\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430\n\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u0440\u044f\u043c\u0438 (\u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435, \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435)\n\u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 (16 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439)\n\u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u0418\u043c\u043f\u043e\u0440\u0442/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 \u0441\u043b\u043e\u0432\u0430\u0440\u0435\u0439 (JSON, CSV)\n\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u0413\u0438\u0431\u043a\u0438\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u0410\u0432\u0442\u043e\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043f\u0440\u0438 \u0432\u0432\u043e\u0434\u0435\n\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 (\u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e)\n\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 EN\u2192RU\n\u0418\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432 \u0441 \u043d\u0435\u0447\u0435\u0442\u043a\u0438\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435\u043c\n\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430\u0440\u0438 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u043e\u0432\n\u041a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u044b\u0439 \u044d\u0440\u0433\u043e\u043d\u043e\u043c\u0438\u0447\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\n\u041b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u0431\u043b\u043e\u043a\u0438\n\u041f\u0430\u043d\u0435\u043b\u044c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0441 \u0438\u043a\u043e\u043d\u043a\u0430\u043c\u0438\n\u041f\u0435\u0440\u0435\u043d\u043e\u0441 \u0440\u0435\u0434\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0432 \u043c\u0435\u043d\u044e\n\u0426\u0432\u0435\u0442\u043e\u0432\u044b\u0435 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u0438 \u044d\u043c\u043e\u0434\u0437\u0438 \u0434\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438\n\u0423\u043b\u0443\u0447\u0448\u0435\u043d\u043d\u0430\u044f \u0447\u0438\u0442\u0430\u0435\u043c\u043e\u0441\u0442\u044c \u0438 \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\n\u0414\u0438\u0430\u043b\u043e\u0433 \u0441\u043f\u0440\u0430\u0432\u043a\u0438 \"\ud83d\udcda \u0421\u043f\u0440\u0430\u0432\u043a\u0430\" \u0441 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0435\u0439 (F1)\n\u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043a\u043d\u043e\u043f\u043a\u0438 \u0432\u044b\u0431\u043e\u0440\u0430 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\n\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u043c\u0435\u0436\u0434\u0443 \u0441\u0435\u0441\u0441\u0438\u044f\u043c\u0438\n\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\n\u041a\u043d\u043e\u043f\u043a\u0438 \"\u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435\" \u0438 \"\u0421\u043d\u044f\u0442\u044c \u0432\u0441\u0435\" \u0432 \u0434\u0438\u0430\u043b\u043e\u0433\u0435\n\u0412\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0434\u0438\u043a\u0430\u0446\u0438\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432\n\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0441\u0442\u043e\u043b\u0431\u0446\u043e\u0432 \u043f\u0440\u0438 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0435\n\u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0435 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0431\u0435\u0437 \u043f\u043e\u0442\u0435\u0440\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\n\u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0430\u044f \u0434\u0435\u0434\u0443\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0438\n\u0414\u0435\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f\n\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\nGUI \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u0430\u043c\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432\n\u043f\u043e\u0438\u0441\u043a \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0431\u0435\u0437 \u0443\u0447\u0435\u0442\u0430 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\n\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043a\u043b\u0430\u0434\u043a\u0430 \"\u041f\u0435\u0440\u0435\u0432\u043e\u0434\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\" \u0441 \u043f\u043e\u043b\u043d\u044b\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c\u0438\n\u0422\u0430\u0431\u043b\u0438\u0446\u0430 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u0438 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f (mm\u2192\u043c\u043c, kg\u2192\u043a\u0433, etc.)\n\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0435\u0434\u0438\u043d\u0438\u0446 \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430\n\u0418\u043c\u043f\u043e\u0440\u0442/\u044d\u043a\u0441\u043f\u043e\u0440\u0442 \u043f\u0440\u0430\u0432\u0438\u043b \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0430 \u0432 JSON \u0444\u043e\u0440\u043c\u0430\u0442\u0435\n\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439\n"},{"assistant_initiates":false,"asst_text":"Sure! Below I'll provide a simple front-end mock-up using plain HTML, CSS, and JavaScript to:\n\n1. **List all available redeem codes** (step 5 condition applied: hide codes when usage exceeds `repeatTime` limit).\n2. **Show the redeem details page with the QR code, checkcode text, seller & product info, and countdown timer for expiration** (step 4).\n\n---\n\n### Assumptions for dummy data:\n\n- Each `redeem` available has:\n  - `id`\n  - `productName`\n  - `sellerName`\n  - `checkcode`\n  - `usedCount` (how many times user already used)\n  - `repeatTime` (limit, 0 means unlimited)\n  - `expireAt` (for the current generated code, i.e. 15 min lifetime)\n  - `codeUsed` (boolean, if current generated code is used or not)\n\n---\n\n## 1. Redeem List Page (`redeem_list.html`)\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\" />\n    <title>Available Redeems</title>\n    <style>\n        body { font-family: Arial, sans-serif; padding: 20px; background: #f7f7f7; }\n        h1 { margin-bottom: 20px; }\n        .redeem-list { max-width: 600px; margin: auto; }\n        .redeem-item {\n            background: #fff;\n            padding: 15px 20px;\n            margin-bottom: 12px;\n            border-radius: 6px;\n            box-shadow: 0 1px 4px rgba(0,0,0,0.1);\n        }\n        .redeem-item h3 { margin: 0 0 8px; }\n        .redeem-item button {\n            margin-top: 10px;\n            background-color: #0078d4;\n            color: white;\n            border: none;\n            border-radius: 4px;\n            padding: 8px 14px;\n            cursor: pointer;\n        }\n        .redeem-item button:disabled {\n            background-color: #aaa;\n            cursor: not-allowed;\n        }\n    </style>\n</head>\n<body>\n    <h1>Your Available Redeems</h1>\n    <div class=\"redeem-list\" id=\"redeemList\"></div>\n\n    <script>\n        // Dummy data (normally would be fetched from backend API)\n        const redeems = [\n            {\n                id: 1,\n                productName: \"Product A Redeem\",\n                sellerName: \"Seller A\",\n                userId: 123,\n                usedCount: 1,\n                repeatTime: 3,\n                codeUsed: false,\n                expireAt: new Date(Date.now() + 10 * 60 * 1000), // expire in 10 mins\n                checkcode: \"ABC123\",\n            },\n            {\n                id: 2,\n                productName: \"Product B Redeem\",\n                sellerName: \"Seller B\",\n                userId: 123,\n                usedCount: 3,\n                repeatTime: 3,\n                codeUsed: true,\n                expireAt: new Date(Date.now() - 1000), // expired\n                checkcode: \"XYZ789\",\n            },\n            {\n                id: 3,\n                productName: \"Product C Redeem\",\n                sellerName: \"Seller C\",\n                userId: 123,\n                usedCount: 0,\n                repeatTime: 0, // unlimited usage\n                codeUsed: false,\n                expireAt: null, // no expire for unused/uncreated code hypothetically\n                checkcode: null,\n            }\n        ];\n\n        const redeemListEl = document.getElementById('redeemList');\n\n        function canUseRedeem(redeem) {\n            // If repeatTime 0 => unlimited\n            if (redeem.repeatTime === 0) return true;\n            return redeem.usedCount < redeem.repeatTime;\n        }\n\n        function renderRedeemList() {\n            redeemListEl.innerHTML = '';\n\n            const availableRedeems = redeems.filter(canUseRedeem);\n\n            if (availableRedeems.length === 0) {\n                redeemListEl.innerHTML = `<p>You have no available redeems at the moment.</p>`;\n                return;\n            }\n\n            availableRedeems.forEach(redeem => {\n                // Check if codeUsed = false && expireAt is still valid => allow direct use\n                // Otherwise allow generate new\n\n                const now = new Date();\n                const stillValid = redeem.codeUsed === false && redeem.expireAt && new Date(redeem.expireAt) > now;\n\n                const buttonText = stillValid ? \"View Redeem\" : \"Generate New Code\";\n\n                const item = document.createElement('div');\n                item.className = 'redeem-item';\n                item.innerHTML = `\n                    <h3>${redeem.productName}</h3>\n                    <p>Seller: ${redeem.sellerName}</p>\n                    <p>Used Times: ${redeem.usedCount} / ${redeem.repeatTime === 0 ? 'Unlimited' : redeem.repeatTime}</p>\n                    <button data-id=\"${redeem.id}\">${buttonText}</button>\n                `;\n                redeemListEl.appendChild(item);\n\n                const btn = item.querySelector('button');\n                btn.onclick = () => {\n                    // Navigate to redeem details page with redeem id\n                    window.location.href = `redeem_detail.html?id=${redeem.id}`;\n                }\n            });\n        }\n\n        renderRedeemList();\n    </script>\n</body>\n</html>\n```\n\n---\n\n## 2. Redeem Details Page (`redeem_detail.html`)\n\n```html\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\" />\n    <title>Redeem Detail</title>\n    <style>\n        body { font-family: Arial, sans-serif; padding: 20px; background: #fefefe; max-width: 400px; margin: auto; }\n        h1 { margin-bottom: 10px; }\n        .detail-box {\n            border: 1px solid #ccc;\n            padding: 20px;\n            border-radius: 8px;\n            text-align: center;\n            box-shadow: 0 1px 5px rgba(0,0,0,0.1);\n        }\n        .product-name { font-weight: bold; font-size: 1.5em; }\n        .seller-name { margin-top: 4px; color: #555; }\n        .qrcode {\n            margin: 20px 0;\n        }\n        .checkcode-text {\n            padding: 8px;\n            background: #eee;\n            border-radius: 4px;\n            font-family: monospace;\n            font-size: 1.2em;\n            user-select: all;\n        }\n        .countdown {\n            margin-top: 15px;\n            font-size: 1.2em;\n            color: #d9534f;\n            font-weight: bold;\n        }\n        .expired {\n            color: #888;\n            font-weight: normal;\n        }\n        button#regenerateBtn {\n            margin-top: 20px;\n            background-color: #0078d4;\n            color: white;\n            border: none;\n            border-radius: 4px;\n            padding: 10px 18px;\n            cursor: pointer;\n            font-size: 1em;\n        }\n        button#regenerateBtn:disabled {\n            background-color: #aaa;\n            cursor: not-allowed;\n        }\n    </style>\n    <script src=\"https://cdn.jsdelivr.net/npm/qrcode\"></script> <!-- QRCode.js CDN -->\n</head>\n<body>\n\n<div class=\"detail-box\" id=\"detailBox\">\n    <h1>Redeem Detail</h1>\n    <div class=\"product-name\" id=\"productName\">Product Name</div>\n    <div class=\"seller-name\" id=\"sellerName\">Seller Name</div>\n    <div class=\"qrcode\" id=\"qrcode\"></div>\n    <div class=\"checkcode-text\" id=\"checkcodeText\">CODE123456</div>\n    <div class=\"countdown\" id=\"countdown\"></div>\n    <button id=\"regenerateBtn\">Regenerate Code</button>\n</div>\n\n<script>\n    // In real app, this data should dynamically load by redeem id (by API/backend)\n    // Here we'll mock it based on query param id\n\n    const urlParams = new URLSearchParams(window.location.search);\n    const redeemId = parseInt(urlParams.get('id')) || 1;\n\n    // Dummy data to illustrate (should be fetched from backend API)\n    const dummyRedeems = {\n        1: {\n            productName: \"Product A Redeem\",\n            sellerName: \"Seller A\",\n            checkcode: \"ABC123XYZ\",\n            usedCount: 1,\n            repeatTime: 3,\n            expireAt: new Date(Date.now() + 9 * 60 * 1000), // 9 mins later\n            codeUsed: false,\n        },\n        2: {\n            productName: \"Product B Redeem\",\n            sellerName: \"Seller B\",\n            checkcode: \"EXPIRED123\",\n            usedCount: 3,\n            repeatTime: 3,\n            expireAt: new Date(Date.now() - 1000), // expired\n            codeUsed: true,\n        },\n        3: {\n            productName: \"Product C Redeem\",\n            sellerName: \"Seller C\",\n            checkcode: null, // no code yet generated\n            usedCount: 0,\n            repeatTime: 0,\n            expireAt: null,\n            codeUsed: false,\n        }\n    };\n\n    const detailBox = document.getElementById('detailBox');\n    const productNameEl = document.getElementById('productName');\n    const sellerNameEl = document.getElementById('sellerName');\n    const qrcodeEl = document.getElementById('qrcode');\n    const checkcodeTextEl = document.getElementById('checkcodeText');\n    const countdownEl = document.getElementById('countdown');\n    const regenerateBtn = document.getElementById('regenerateBtn');\n\n    let intervalId;\n    let currentExpireAt;\n    let currentCheckcode;\n    let codeUsed;\n    let usedCount;\n    let repeatTime;\n\n    function generateNewCode() {\n        // Normally: call backend API to generate new checkcode, create new record, get expireAt\n        // Here just generate a random string + 15 mins expire time\n\n        currentCheckcode = Math.random().toString(36).substring(2, 10).toUpperCase();\n        currentExpireAt = new Date(Date.now() + 15 * 60 * 1000); // 15 min\n        codeUsed = false;\n        usedCount++;\n        renderPage();\n    }\n\n    function startCountdown(expireAt) {\n        clearInterval(intervalId);\n\n        function updateTimer() {\n            let now = new Date();\n            let diff = expireAt - now;\n            if(diff <= 0) {\n                clearInterval(intervalId);\n                countdownEl.textContent = \"Code expired!\";\n                countdownEl.classList.add('expired');\n                regenerateBtn.disabled = false;\n                codeUsed = true; // simulate expiration\n                renderQRCode(null);\n                checkcodeTextEl.textContent = \"-\";\n                return;\n            }\n            countdownEl.textContent = `Expires in: ${Math.floor(diff/60000)}m ${Math.floor((diff%60000)/1000)}s`;\n            countdownEl.classList.remove('expired');\n            regenerateBtn.disabled = true;\n        }\n\n        updateTimer();\n        intervalId = setInterval(updateTimer, 1000);\n    }\n\n    function renderQRCode(text) {\n        qrcodeEl.innerHTML = \"\";\n        if (!text) return;\n        new QRCode(qrcodeEl, {\n            text: text,\n            width: 180,\n            height: 180,\n            colorDark : \"#000000\",\n            colorLight : \"#ffffff\",\n        });\n    }\n\n    function renderPage() {\n        const redeem = dummyRedeems[redeemId];\n\n        productNameEl.textContent = redeem.productName;\n        sellerNameEl.textContent = redeem.sellerName;\n\n        if (redeem.checkcode && !codeUsed) {\n            checkcodeTextEl.textContent = currentCheckcode ?? redeem.checkcode;\n            renderQRCode(currentCheckcode ?? redeem.checkcode);\n            startCountdown(currentExpireAt ?? new Date(redeem.expireAt));\n            regenerateBtn.disabled = true;\n        } else {\n            // No valid code exists or expired/used\n            checkcodeTextEl.textContent = \"-\";\n            renderQRCode(null);\n            countdownEl.textContent = \"No available code. Please regenerate.\";\n            countdownEl.classList.add('expired');\n            regenerateBtn.disabled = false;\n        }\n\n        // Disable regenerate button if repeatTime reached\n        if (redeem.repeatTime !== 0 && usedCount >= redeem.repeatTime) {\n            regenerateBtn.disabled = true;\n            countdownEl.textContent = \"Redeem limit reached.\";\n            countdownEl.classList.add('expired');\n        }\n    }\n\n    regenerateBtn.addEventListener('click', () => {\n        const redeem = dummyRedeems[redeemId];\n        if (redeem.repeatTime !== 0 && usedCount >= redeem.repeatTime) {\n            alert(\"You have reached the maximum usage limit for this redeem.\");\n            return;\n        }\n\n        generateNewCode();\n    });\n\n    // Initialize\n    (function init() {\n        const redeem = dummyRedeems[redeemId];\n        if (!redeem) {\n            detailBox.innerHTML = \"<p>Invalid redeem id.</p>\";\n            return;\n        }\n\n        currentCheckcode = redeem.checkcode;\n        currentExpireAt = new Date(redeem.expireAt);\n        codeUsed = redeem.codeUsed;\n        usedCount = redeem.usedCount;\n        repeatTime = redeem.repeatTime;\n\n        renderPage();\n    })();\n</script>\n\n</body>\n</html>\n```\n\n---\n\n### How to Use\n\n- Save both files `redeem_list.html` and `redeem_detail.html` in your front-end directory.\n- Open `redeem_list.html`, which shows the available redeems based on dummy data and conditions.\n- Clicking each redeem item leads to `redeem_detail.html?id=REDEEM_ID` which shows the QR code & countdown timer for the checkcode.\n- The \"Regenerate Code\" button simulates generating a new redeem code with 15 minutes expiration and disables itself until expiry.\n\n---\n\n### Final Notes for Integration\n\n- Replace the dummy data with your actual API calls to fetch redeem info/get/generate checkcodes.\n- Ensure the count, status, and expiry are validated and updated from backend accordingly.\n- Use a QR code generation library you prefer on the backend or front-end (I used [davidshimjs/qrcodejs](https://github.com/davidshimjs/qrcodejs) CDN here).\n- Add user authentication and protect redeem pages as needed.\n\nIf you want, I can also help you create the backend API endpoints for these flows! Just ask!","behavior":"degenerate_repetition","conversation_hash":"7a2156283509546788644f7f2cdff92d","conversation_redacted":false,"conversation_toxic":false,"country":"Hong Kong","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"English","model":"gpt-4.1-mini-2025-04-14","n_chars":16900,"n_turns_in_conversation":1,"n_words":1869,"source_row_offset":32597,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 08:09:28 GMT","turn_id":"99c85e40d52625c1d6bdb2cb","turn_index":0,"user_text":"I am developing the online web shop with CodeIgniter 4 framework and making the redeem system to allow the user to use the redeem code.\nI have complete the database structure as the following:\nexternal_promo_code, for store the root data of the redeem product:\n \t# \t\u540d\u7a31 \t\u985e\u578b \t\u7de8\u78bc\u8207\u6392\u5e8f \t\u5c6c\u6027 \t\u7a7a\u503c(Null) \t\u9810\u8a2d\u503c \t\u5099\u8a3b \t\u984d\u5916\u8cc7\u8a0a \t\u52d5\u4f5c\n\t1 \tid \u4e3b\u9375 \tint(11) \t\t\t\u5426 \t\u7121 \t\tAUTO_INCREMENT \t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t2 \tpid \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t(product_id)\n\t3 \tstartDate \tdatetime \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t4 \tendDate \tdatetime \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t5 \tperiod \tint(11) \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t6 \trepeatTime \tint(11) \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t7 \tcreated_at \tdatetime \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t8 \tupdated_at \tdatetime \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\nexternal_promo_redeem, for store the distrubuted redeem code (the code belong to the user):\n \t# \t\u540d\u7a31 \t\u985e\u578b \t\u7de8\u78bc\u8207\u6392\u5e8f \t\u5c6c\u6027 \t\u7a7a\u503c(Null) \t\u9810\u8a2d\u503c \t\u5099\u8a3b \t\u984d\u5916\u8cc7\u8a0a \t\u52d5\u4f5c\n\t1 \tid \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t2 \tproId \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t(id from external_promo_code)\n\t3 \tuserId \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t(purchased user id)\n\t4 \tstarttime \tdatetime \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t5 \tendtime \tdatetime \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t6 \trepeatTime \tint(11) \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t(0=unlimited)\n\t7 \tdistrubuteDate \tdatetime \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t8 \tstatus \tint(11) \t\t\t\u5426 \t1 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\nexternal_promo_redeem_records, for store the redeemed code record:\n \t# \t\u540d\u7a31 \t\u985e\u578b \t\u7de8\u78bc\u8207\u6392\u5e8f \t\u5c6c\u6027 \t\u7a7a\u503c(Null) \t\u9810\u8a2d\u503c \t\u5099\u8a3b \t\u984d\u5916\u8cc7\u8a0a \t\u52d5\u4f5c\n\t1 \tid \u4e3b\u9375 \tint(11) \t\t\t\u5426 \t\u7121 \t\tAUTO_INCREMENT \t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t2 \tproId \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t3 \tredeemId \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t(id from external_promo_redeem)\n\t4 \tuserId \tint(11) \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t5 \tdateTime \tdatetime \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t6 \texpiredDate \tdatetime \t\t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t7 \tcheckcode \ttext \tutf8mb4_unicode_ci \t\t\u5426 \t\u7121 \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t8 \tstatus \tint(11) \t\t\t\u5426 \t1 \t1=available 0=used \t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t9 \tusedDate \tdatetime \t\t\t\u662f \tcurrent_timestamp() \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\t10 \tverifiedSellerID \tint(11) \t\t\t\u662f \tNULL \t\t\t\u4fee\u6539 \u4fee\u6539 \t\u522a\u9664 \u522a\u9664 \t\n\n\nThe flow for the redeem code:\n1. The seller will setup the product with the type of redeem and related setting (i.e. repeatTIme, startDate, endDate, period) in the management panel, the redeem data will be store in the external_promo_code as the root data.\n2. the user can buy the product to get the redeem code, the related purchased redeem code will be store in external_promo_redeem, and will be show in the front-end page to let the user to check and use the redeem\n3. the user can use the redeem in the front-end page, the record with checkcode will be generated and stored to external_promo_redeem_records, user is required to use the code within 15 mins, otherwise will be expired and the user need to use the redeem to regenerate the code again.\n4. user can browse the available generated code again by redeem the same code until it become expired. User will see the page with the details of redeem (such as the name, seller name), the QRCode with the checkcode data, text of checkcode, and the countdown timer for expire\n5. if the used time (the count of generated code with status 0 of the same user) of redeem exceed the repeat time set in external_promo_code, the user cannot use the redeem and should be hidden in the front-end page\n\nCan you first design the front-end page to list all the avaliable redeem with the dummy data, and the redeem page to fulfill the flow step no.4?"},{"assistant_initiates":false,"asst_text":"Sure! Here's a corrected version of your sentence:\n\nHello John,  \nI agree. I\u2019ll check each line item to see where admin support can be booked.","behavior":"degenerate_repetition","conversation_hash":"e96398c3e03a17df741dd50e87257069","conversation_redacted":false,"conversation_toxic":false,"country":"Philippines","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"English","model":"gpt-4.1-mini-2025-04-14","n_chars":250,"n_turns_in_conversation":2,"n_words":43,"source_row_offset":32598,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 08:09:46 GMT","turn_id":"71b669f992106d19bbd9daed","turn_index":0,"user_text":"please correct; Hello John,\nI Agree. I\u2019ll check per line items where admin support can be booked against. \n"},{"assistant_initiates":false,"asst_text":"Certainly! Here's a corrected version of your sentence:\n\nHello Sir Gil,  \nPlease review the attached quotation for the aft ramp. This scope covers the remaining area for preservation left by ENN.","behavior":"degenerate_repetition","conversation_hash":"e96398c3e03a17df741dd50e87257069","conversation_redacted":false,"conversation_toxic":false,"country":"Philippines","empty_output":false,"evidence_codes":[],"label":"absent","label_present":false,"label_qualified":false,"label_source":"Final dense release","language":"English","model":"gpt-4.1-mini-2025-04-14","n_chars":338,"n_turns_in_conversation":2,"n_words":54,"source_row_offset":32599,"source_shard":"turns-00084.parquet","timestamp":"Tue, 01 Jul 2025 08:09:46 GMT","turn_id":"a988aebf9dce9f757d580f5a","turn_index":1,"user_text":"please correct: Hello Sir Gil,\nPlease review the attached quotation for aft ramp. This scope the remained area for preservation left by ENN. \n"}],"shard":{"index":84,"name":"turns-00084.parquet","rows":46857}}
