diff --git a/assets/controllers/elements/nonprintable_char_input_controller.js b/assets/controllers/elements/nonprintable_char_input_controller.js
deleted file mode 100644
index bd172f1b..00000000
--- a/assets/controllers/elements/nonprintable_char_input_controller.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
- *
- * Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import {Controller} from "@hotwired/stimulus";
-
-/**
- * Purpose of this controller is to allow users to input non-printable characters like EOT, FS, etc. in a form field and submit them correctly with the form.
- * The visible input field encodes non-printable characters via their Unicode Control picture representation, e.g. \n becomes ␊ and \t becomes ␉, so that they can be displayed in the input field without breaking the form submission.
- * The actual value of the field, which is submitted with the form, is stored in a hidden input and contains the non-printable characters in their original form.
- */
-export default class extends Controller {
-
- _hiddenInput;
-
- connect() {
- this.element.addEventListener("input", this._update.bind(this));
-
- // We use a hidden input to store the actual value of the field, which is submitted with the form.
- // The visible input is just for user interaction and can contain non-printable characters, which are not allowed in the hidden input.
- this._hiddenInput = document.createElement("input");
- this._hiddenInput.type = "hidden";
- this._hiddenInput.name = this.element.name;
- this.element.removeAttribute("name");
- this.element.parentNode.insertBefore(this._hiddenInput, this.element.nextSibling);
-
- this.element.addEventListener("keypress", this._onKeyPress.bind(this));
- }
-
- /**
- * Ensures that non-printable characters like EOT, FS, etc. gets added to the input value when the user types them
- * @param event
- * @private
- */
- _onKeyPress(event) {
- const ALLOWED_INPUT_CODES = [4, 28, 29, 30, 31]; //EOT, FS, GS, RS, US
-
- if (!ALLOWED_INPUT_CODES.includes(event.keyCode)) {
- return;
- }
-
- event.preventDefault();
-
- const char = String.fromCharCode(event.keyCode);
- this.element.value += char;
-
- this._update();
-
-
- }
-
- _update() {
- //Chrome workaround: Remove a leading ∠ character (U+2220) that appears when the user types a non-printable character at the beginning of the input field.
- if (this.element.value.startsWith("∠")) {
- this.element.value = this.element.value.substring(1);
- }
-
- // Remove non-printable characters from the input value and store them in the hidden input
- const normalizedValue = this.decodeNonPrintableChars(this.element.value);
- this._hiddenInput.value = normalizedValue;
-
- // Encode non-printable characters in the visible input to their Unicode Control picture representation
- const encodedValue = this.encodeNonPrintableChars(normalizedValue);
- if (encodedValue !== this.element.value) {
- this.element.value = encodedValue;
- }
- }
-
- /**
- * Encodes non-printable characters in the given string via their Unicode Control picture representation, e.g. \n becomes ␊ and \t becomes ␉.
- * This allows us to display non-printable characters in the input field without breaking the form submission.
- * @param str
- */
- encodeNonPrintableChars(str) {
- return str.replace(/[\x00-\x1F\x7F]/g, (char) => {
- const code = char.charCodeAt(0);
- return String.fromCharCode(0x2400 + code);
- });
- }
-
- /**
- * Decodes the Unicode Control picture representation of non-printable characters back to their original form, e.g. ␊ becomes \n and ␉ becomes \t.
- * @param str
- */
- decodeNonPrintableChars(str) {
- return str.replace(/[\u2400-\u241F\u2421]/g, (char) => {
- const code = char.charCodeAt(0) - 0x2400;
- return String.fromCharCode(code);
- });
- }
-}
diff --git a/assets/controllers/helpers/scan_special_char_controller.js b/assets/controllers/helpers/scan_special_char_controller.js
deleted file mode 100644
index 154b2a94..00000000
--- a/assets/controllers/helpers/scan_special_char_controller.js
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
- *
- * Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-import { Controller } from "@hotwired/stimulus"
-
-/**
- * This controller listens for a special non-printable character (SOH / ASCII 1) to be entered anywhere on the page,
- * which is then used as a trigger to submit the following characters as a barcode / scan input.
- */
-export default class extends Controller {
- connect() {
- // Optional: Log to confirm global attachment
- console.log("Scanner listener active")
-
- this.isCapturing = false
- this.buffer = ""
-
- window.addEventListener("keypress", this.handleKeydown.bind(this))
- }
-
- initialize() {
- this.isCapturing = false
- this.buffer = ""
- this.timeoutId = null
- }
-
- handleKeydown(event) {
-
- // Ignore if the user is typing in a form field
- const isInput = ["INPUT", "TEXTAREA", "SELECT"].includes(event.target.tagName) ||
- event.target.isContentEditable;
- if (isInput) return
-
- // 1. Detect Start of Header (SOH / Ctrl+A)
- if (event.key === "\x01" || event.keyCode === 1) {
- this.startCapturing(event)
- return
- }
-
- // 2. Process characters if in capture mode
- if (this.isCapturing) {
- this.resetTimeout() // Push the expiration back with every keypress
-
- if (event.key === "Enter" || event.keyCode === 13) {
-
- this.finishCapturing(event)
- } else if (event.key.length === 1) {
- this.buffer += event.key
- }
- }
- }
-
- startCapturing(event) {
- this.isCapturing = true
- this.buffer = ""
- this.resetTimeout()
- event.preventDefault()
- console.debug("Scan character detected. Capture started...")
- }
-
- finishCapturing(event) {
- event.preventDefault()
- const data = this.buffer;
- this.stopCapturing()
- this.processCapture(data)
- }
-
- stopCapturing() {
- this.isCapturing = false
- this.buffer = ""
- if (this.timeoutId) clearTimeout(this.timeoutId)
- console.debug("Capture cleared/finished.")
- }
-
- resetTimeout() {
- if (this.timeoutId) clearTimeout(this.timeoutId)
-
- this.timeoutId = setTimeout(() => {
- if (this.isCapturing) {
- console.warn("Capture timed out. Resetting buffer.")
- this.stopCapturing()
- }
- }, 500)
- }
-
- processCapture(data) {
- if (!data) return
-
- console.debug("Captured scan data: " + data)
-
- const scanInput = document.getElementById("scan_dialog_input");
- if (scanInput) { //When we are on the scan dialog page, submit the form there
- this._submitScanForm(data);
- } else { //Otherwise use our own form (e.g. on the part list page)
- this.element.querySelector("input[name='input']").value = data;
- this.element.requestSubmit();
- }
-
-
- }
-
- _submitScanForm(data) {
- const scanInput = document.getElementById("scan_dialog_input");
- if (!scanInput) {
- console.error("Scan input field not found!")
- return;
- }
-
- scanInput.value = data;
- scanInput.dispatchEvent(new Event('input', { bubbles: true }));
-
- const form = document.getElementById("scan_dialog_form");
- if (!form) {
- console.error("Scan form not found!")
- return;
- }
-
- form.requestSubmit();
- }
-}
diff --git a/assets/controllers/pages/barcode_scan_controller.js b/assets/controllers/pages/barcode_scan_controller.js
index bdc9c78c..ae51e951 100644
--- a/assets/controllers/pages/barcode_scan_controller.js
+++ b/assets/controllers/pages/barcode_scan_controller.js
@@ -114,11 +114,7 @@ export default class extends Controller {
// Mark as handled immediately (prevents spam even if callback fires repeatedly)
this._lastDecodedText = normalized;
- const input = document.getElementById('scan_dialog_input');
- input.value = decodedText;
- //Trigger nonprintable char input controller to update the hidden input value
- input.dispatchEvent(new Event('input', { bubbles: true }));
-
+ document.getElementById('scan_dialog_input').value = decodedText;
//Submit form
document.getElementById('scan_dialog_form').requestSubmit();
}
diff --git a/docs/usage/scanner.md b/docs/usage/scanner.md
deleted file mode 100644
index 47b3feff..00000000
--- a/docs/usage/scanner.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-title: Barcode Scanner
-layout: default
-parent: Usage
----
-
-# Barcode scanner
-
-When the user has the correct permission there will be a barcode scanner button in the navbar.
-On this page you can either input a barcode code by hand, use an external barcode scanner, or use your devices camera to
-scan a barcode.
-
-In info mode (when the "Info" toggle is enabled) you can scan a barcode and Part-DB will parse it and show information
-about it.
-
-Without info mode, the barcode will directly redirect you to the corresponding page.
-
-### Barcode matching
-
-When you scan a barcode, Part-DB will try to match it to an existing part, part lot or storage location first.
-For Part-DB generated barcodes, it will use the internal ID of a part. Alternatively you can also scan a barcode that contains the part's IPN.
-
-You can set a GTIN/EAN code in the part properties and Part-DB will open the part page when you scan the corresponding GTIN/EAN barcode.
-
-On a part lot you can under "Advanced" set a user barcode, that will redirect you to the part lot page when scanned. This allows to reuse
-arbitrary existing barcodes that already exist on the part lots (for example, from the manufacturer) and link them to the part lot in Part-DB.
-
-Part-DB can also parse various distributor barcodes (for example from Digikey and Mouser) and will try to redirect you to the corresponding
-part page based on the distributor part number in the barcode.
-
-### Part creation from barcodes
-For certain barcodes Part-DB can automatically create a new part, when it cannot find a matching part.
-Part-DB will try to retrieve the part information from an information provider and redirects you to the part creation page
-with the retrieved information pre-filled.
-
-## Using an external barcode scanner
-
-Part-DB supports the use of external barcode scanners that emulate keyboard input. To use a barcode scanner with Part-DB,
-simply connect the scanner to your computer and scan a barcode while the cursor is in a text field in Part-DB.
-The scanned barcode will be entered into the text field as if you had typed it on the keyboard.
-
-In scanner fields, it will also try to insert special non-printable characters the scanner send via Alt + key combinations.
-This is required for EIGP114 datamatrix codes.
-
-### Automatically redirect on barcode scanning
-
-If you configure your barcode scanner to send a (Start of heading, 0x01) non-printable character at the beginning
-of the scanned barcode, Part-DB will automatically scan the barcode that comes afterward (and is ended with an enter key)
-and redirects you to the corresponding page.
-This allows you to quickly scan a barcode from anywhere in Part-DB without the need to first open the scanner page.
-If an input field is focused, the barcode will be entered into the field as usual and no redirection will happen.
diff --git a/src/Form/LabelSystem/ScanDialogType.php b/src/Form/LabelSystem/ScanDialogType.php
index 5f9ce65f..cd1440c6 100644
--- a/src/Form/LabelSystem/ScanDialogType.php
+++ b/src/Form/LabelSystem/ScanDialogType.php
@@ -61,8 +61,6 @@ class ScanDialogType extends AbstractType
'attr' => [
'autofocus' => true,
'id' => 'scan_dialog_input',
- 'style' => 'font-family: var(--bs-font-monospace)',
- 'data-controller' => 'elements--nonprintable-char-input',
],
]);
diff --git a/templates/base.html.twig b/templates/base.html.twig
index afc7a8bf..62f0ce53 100644
--- a/templates/base.html.twig
+++ b/templates/base.html.twig
@@ -2,7 +2,7 @@
@@ -73,17 +73,9 @@
{{ encore_entry_script_tags('webauthn_tfa') }}
{% endblock %}
-
-
-{# Listen for the special #}
-{% if is_granted("@tools.label_scanner") %}
-
-{% endif %}
-
{% block body %}
@@ -129,13 +121,13 @@
{# Must be outside of the sidebar or it will be hidden too #}