diff --git a/src/components/ScenarioEditor.svelte b/src/components/ScenarioEditor.svelte
new file mode 100644
index 0000000..646fc35
--- /dev/null
+++ b/src/components/ScenarioEditor.svelte
@@ -0,0 +1,325 @@
+
+
+
+
+
+
+
+
+
{@html highlightedHtml}
+
+
+
+
+ {#if validation.errors.length > 0}
+
+
Ошибки синтаксиса:
+
+ {#each validation.errors as err}
+ - Строка {err.line}: {err.message}
+ {/each}
+
+
+ {/if}
+
+
+
diff --git a/src/lib/WebSocketManager.js b/src/lib/WebSocketManager.js
index 02e8fdb..a398226 100644
--- a/src/lib/WebSocketManager.js
+++ b/src/lib/WebSocketManager.js
@@ -8,6 +8,7 @@ import * as deviceConnection from "./deviceConnection.js";
import * as deviceListManager from "./deviceListManager.js";
import * as blobProtocol from "./blobProtocol.js";
import * as wsReconnect from "./wsReconnect.js";
+import { sanitizeScenario } from "./scenarioUtils.js";
import { eventEmitter } from "../eventEmitter.js";
const LOG_MAX_MESSAGES = 100;
@@ -536,7 +537,7 @@ export default class WebSocketManager {
this.wsSendMsg(this.selectedWs, "/tuoyal|" + JSON.stringify(this.generateLayout()));
this._modify();
this.wsSendMsg(this.selectedWs, "/gifnoc|" + JSON.stringify(this.configJson));
- this.wsSendMsg(this.selectedWs, "/oiranecs|" + this.scenarioTxt);
+ this.wsSendMsg(this.selectedWs, "/oiranecs|" + sanitizeScenario(this.scenarioTxt));
this.clearData();
this.sendCurrentPageNameToSelectedWs();
}
diff --git a/src/lib/scenarioUtils.js b/src/lib/scenarioUtils.js
new file mode 100644
index 0000000..808feb5
--- /dev/null
+++ b/src/lib/scenarioUtils.js
@@ -0,0 +1,23 @@
+/**
+ * Scenario text sanitization for IoTManager backend compatibility.
+ * Matches IoTScenario.cpp lexer: file is read byte-by-byte; isspace() and ';' are skipped;
+ * comments # to EOL; strings "..."; identifiers [a-zA-Z_][a-zA-Z0-9_]*; other ASCII as tokens.
+ * We normalize line endings and remove control chars that would break the parser.
+ */
+
+/**
+ * Sanitize scenario text before sending to device (/oiranecs|).
+ * - Normalize line endings to LF (\n): \r\n and \r -> \n
+ * - Remove control characters (0x00-0x1F) except \n (10) and \t (9)
+ * So no NUL, no other control chars; backend expects printable ASCII + \n \t in code.
+ * @param {string} text - raw scenario text
+ * @returns {string} sanitized text safe for IoTScenario parser
+ */
+export function sanitizeScenario(text) {
+ if (text == null || typeof text !== "string") return "";
+ // Normalize line endings to \n only (backend counts curLine on LastChar == 10)
+ let out = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
+ // Remove control chars except \t (9) and \n (10)
+ out = out.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
+ return out;
+}
diff --git a/src/pages/Config.svelte b/src/pages/Config.svelte
index 1d46c3b..6422c7b 100644
--- a/src/pages/Config.svelte
+++ b/src/pages/Config.svelte
@@ -1,5 +1,6 @@