feat: move snippets, keybinding actions and macros out of core into plugins

This commit is contained in:
LukeGus committed 2026-09-29 11:39:14 -05:00
1 parent d9bba10612
commit e0c30a4e6f
262 files changed
+9091 -10744

No files matched your search

+119 -2
View File
@@ -490,7 +490,6 @@
"savedThemesLabel": "Saved Themes",
"scrollbackBufferLabel": "Scrollback Buffer",
"scrollbackMaxLines": "Maximum number of lines kept in history",
"startupSnippetLabel": "Startup Snippet",
"sudoPasswordAutoFill": "Sudo Password Auto-Fill",
"sudoPasswordAutoFillDesc": "Store a sudo password so host metrics, terminal prompts, and others can run privileged commands automatically.",
"sudoPasswordAutoFillLabel": "Sudo Password Auto-fill",
@@ -520,7 +519,8 @@
"history": "History",
"localTerminal": "Local Terminal",
"sshTools": "SSH Tools",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Type here to broadcast keystrokes...",
@@ -551,5 +551,122 @@
"savedThemePlaceholder": "Choose a saved theme",
"title": "Connection Defaults",
"apply": "Apply"
},
"keybindings": {
"copy": "Copy selection",
"paste": "Paste",
"sendControlCode": "Send Ctrl+letter signal",
"sendText": "Send text",
"controlCodeLabel": "Letter",
"textLabel": "Text to send",
"appendEnterLabel": "Press Enter after sending",
"clipboardPermissionNote": "May prompt for clipboard permission on first use in some browsers.",
"textRequired": "Enter the text to send",
"controlCodeRequired": "Enter a single letter for the control code",
"builtIn": {
"copyCtrlC": "Copy selection (Ctrl+C, only when text is selected)",
"copyCtrlShiftC": "Copy selection (Ctrl+Shift+C)",
"copyCmdC": "Copy selection (Cmd+C)",
"pasteCtrlShiftV": "Paste from clipboard (Ctrl+Shift+V)",
"ctrlW": "Send Ctrl+W to shell (blocked from closing browser tab)",
"ctrlT": "Send Ctrl+T to shell (blocked from opening browser tab)",
"ctrlN": "Send Ctrl+N to shell (blocked from opening browser window)",
"ctrlQ": "Send Ctrl+Q to shell (blocked from quitting browser)"
}
},
"macros": {
"title": "Macros",
"description": "Run a saved series of commands in a terminal, and react to what the server sends back.",
"target": "Target",
"noTerminal": "No terminal open",
"empty": "No macros yet",
"emptyHint": "Create a macro to automate a terminal flow, like logging in, restarting a service, or answering prompts.",
"create": "New macro",
"back": "Back to macros",
"name": "Name",
"namePlaceholder": "Restart web server",
"nameRequired": "Give the macro a name before saving",
"descriptionLabel": "Description",
"descriptionPlaceholder": "What this macro does (optional)",
"unsaved": "Unsaved changes",
"save": "Save",
"saved": "Macro saved",
"saveFailed": "Could not save the macro",
"delete": "Delete",
"deleteTitle": "Delete this macro?",
"deleteBody": "\"{{name}}\" will be permanently deleted. This cannot be undone.",
"deleted": "Macro deleted",
"cancel": "Cancel",
"run": "Run",
"stop": "Stop",
"running": "Running...",
"completed": "Macro finished",
"failed": "Macro failed",
"loadFailed": "Could not load macros",
"needTerminal": "Open and connect a terminal first",
"noSteps": "This macro has no steps yet. Add one below.",
"addStep": "Add step",
"stepNumber": "Step {{number}}",
"removeStep": "Remove step",
"confirmRunTitle": "Run this macro?",
"confirmRunBody": "\"{{name}}\" will send commands to {{host}}.",
"confirmRunAction": "Run macro",
"stepTypes": {
"send": {
"label": "Send command",
"help": "Types text into the terminal, the same as if you typed it."
},
"wait": {
"label": "Wait for output",
"help": "Pauses until the server prints the text you name, like a password prompt."
},
"delay": {
"label": "Pause",
"help": "Waits a fixed amount of time before the next step."
},
"if": {
"label": "If output contains",
"help": "Checks everything the server has printed so far, then runs one of two branches."
},
"repeat": {
"label": "Repeat",
"help": "Runs the steps inside it a set number of times."
}
},
"send": {
"text": "Command or text",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Press Enter after sending",
"pressEnterHelp": "Turn this off to type text without submitting it."
},
"wait": {
"pattern": "Wait until the output contains",
"patternPlaceholder": "password:",
"timeout": "Give up after",
"onTimeout": "If it never appears",
"stop": "Stop the macro",
"continue": "Keep going anyway"
},
"delay": {
"duration": "Wait for"
},
"ifStep": {
"pattern": "If the output contains",
"patternPlaceholder": "Active: active (running)",
"then": "Then do this",
"else": "Otherwise do this",
"emptyBranch": "No steps"
},
"repeat": {
"count": "Number of times",
"times": "times"
},
"match": {
"useRegex": "Match with a regular expression",
"useRegexHelp": "Leave this off to match the text exactly as written.",
"invalidRegex": "Not a valid regular expression, it will be matched as plain text"
},
"seconds": "seconds",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Gestoorde temas",
"scrollbackBufferLabel": "Terugrolbuffer",
"scrollbackMaxLines": "Maksimum aantal lyne wat in die geskiedenis gehou word",
"startupSnippetLabel": "Opstart-brokkie",
"sudoPasswordAutoFill": "Sudo Wagwoord Outomatiese Vul",
"sudoPasswordAutoFillDesc": "Stoor 'n sudo-wagwoord sodat gasheermetrieke, terminaalprompts en ander outomaties bevoorregte opdragte kan uitvoer.",
"sudoPasswordAutoFillLabel": "Sudo Wagwoord Outomatiese Vul",
@@ -335,7 +334,8 @@
"history": "Geskiedenis",
"localTerminal": "Plaaslike Terminaal",
"sshTools": "SSH-gereedskap",
"terminal": "Terminaal"
"terminal": "Terminaal",
"macros": "Makro's"
},
"sshTools": {
"broadcastInputPlaceholder": "Tik hier om toetsaanslagen uit te saai...",
@@ -363,5 +363,112 @@
"off": "Af",
"on": "Aan",
"title": "Verbindingsverstekke"
},
"keybindings": {
"copy": "Kopieer seleksie",
"paste": "Plak",
"sendControlCode": "Stuur Ctrl+letter sein",
"sendText": "Stuur SMS",
"controlCodeLabel": "Brief",
"textLabel": "SMS om te stuur",
"appendEnterLabel": "Druk Enter na stuur",
"clipboardPermissionNote": "Kan in sommige blaaiers met die eerste gebruik vir knipbordtoestemming vra.",
"textRequired": "Voer die teks in om te stuur",
"controlCodeRequired": "Voer 'n enkele letter vir die beheerkode in"
},
"macros": {
"title": "Makro's",
"description": "Voer 'n gestoorde reeks opdragte in 'n terminaal uit en reageer op wat die bediener terugstuur.",
"target": "Teiken",
"noTerminal": "Geen terminaal oop nie",
"empty": "Geen makro's nog nie",
"emptyHint": "Skep 'n makro om 'n terminaalvloei te outomatiseer, soos om aan te meld, 'n diens te herbegin of aanwysings te beantwoord.",
"create": "Nuwe makro",
"back": "Terug na makro's",
"name": "Naam",
"namePlaceholder": "Herbegin die webbediener",
"nameRequired": "Gee die makro 'n naam voordat jy dit stoor",
"descriptionLabel": "Beskrywing",
"descriptionPlaceholder": "Wat hierdie makro doen (opsioneel)",
"unsaved": "Ongestoorde veranderinge",
"save": "Stoor",
"saved": "Makro gestoor",
"saveFailed": "Kon nie die makro stoor nie",
"delete": "Vee uit",
"deleteTitle": "Vee hierdie makro uit?",
"deleteBody": "\"{{name}}\" sal permanent verwyder word. Dit kan nie ongedaan gemaak word nie.",
"deleted": "Makro uitgevee",
"cancel": "Kanselleer",
"run": "Hardloop",
"stop": "Stop",
"running": "Hardloop...",
"completed": "Makro klaar",
"failed": "Makro het misluk",
"loadFailed": "Kon nie makro's laai nie",
"needTerminal": "Maak eers 'n terminaal oop en koppel dit",
"noSteps": "Hierdie makro het nog geen stappe nie. Voeg een hieronder by.",
"addStep": "Voeg stap by",
"stepNumber": "Stap {{number}}",
"removeStep": "Verwyder stap",
"confirmRunTitle": "Voer hierdie makro uit?",
"confirmRunBody": "\"{{name}}\" sal bevele stuur na {{host}}.",
"confirmRunAction": "Begin makro",
"stepTypes": {
"send": {
"label": "Stuur opdrag",
"help": "Tik teks in die terminaal, dieselfde asof jy dit getik het."
},
"wait": {
"label": "Wag vir uitvoer",
"help": "Pouseer totdat die bediener die teks wat jy noem, druk, soos 'n wagwoordaanwysing."
},
"delay": {
"label": "Pouseer",
"help": "Wag 'n vaste hoeveelheid tyd voor die volgende stap."
},
"if": {
"label": "Indien die uitvoer bevat",
"help": "Kontroleer alles wat die bediener tot dusver gedruk het, en voer dan een van twee takke uit."
},
"repeat": {
"label": "Herhaal",
"help": "Voer die stappe daarin 'n vasgestelde aantal kere uit."
}
},
"send": {
"text": "Opdrag of teks",
"textPlaceholder": "systemctl herbegin nginx",
"pressEnter": "Druk Enter na stuur",
"pressEnterHelp": "Skakel dit af om teks te tik sonder om dit in te dien."
},
"wait": {
"pattern": "Wag totdat die uitvoer bevat",
"patternPlaceholder": "wagwoord:",
"timeout": "Gee op daarna",
"onTimeout": "As dit nooit verskyn nie",
"stop": "Stop die makro",
"continue": "Gaan in elk geval voort"
},
"delay": {
"duration": "Wag vir"
},
"ifStep": {
"pattern": "Indien die uitvoer bevat",
"patternPlaceholder": "Aktief: aktief (hardloop)",
"then": "Doen dan hierdie",
"else": "Andersins doen dit",
"emptyBranch": "Geen stappe nie"
},
"repeat": {
"count": "Aantal kere",
"times": "tye"
},
"match": {
"useRegex": "Stem ooreen met 'n gereelde uitdrukking",
"useRegexHelp": "Los dit af om die teks presies soos geskryf te laat ooreenstem.",
"invalidRegex": "Nie 'n geldige gereelde uitdrukking nie, dit sal as gewone teks ooreenstem"
},
"seconds": "sekondes",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "السمات المحفوظة",
"scrollbackBufferLabel": "مخزن التمرير المؤقت",
"scrollbackMaxLines": "الحد الأقصى لعدد الأسطر المحفوظة في السجل",
"startupSnippetLabel": "مقتطف بدء التشغيل",
"sudoPasswordAutoFill": "التعبئة التلقائية لكلمة مرور Sudo",
"sudoPasswordAutoFillDesc": "تخزين كلمة مرور sudo لتشغيل مقاييس المضيف ومطالبات الطرفية وغيرها من الأوامر المميزة تلقائياً.",
"sudoPasswordAutoFillLabel": "الملء التلقائي لكلمة مرور sudo",
@@ -335,7 +334,8 @@
"history": "السجل",
"localTerminal": "المحطة المحلية",
"sshTools": "أدوات SSH",
"terminal": "الطرفية"
"terminal": "الطرفية",
"macros": "وحدات الماكرو"
},
"sshTools": {
"broadcastInputPlaceholder": "اكتب هنا لبث ضغطات المفاتيح...",
@@ -363,5 +363,112 @@
"off": "عن",
"on": "على",
"title": "الإعدادات الافتراضية للاتصال"
},
"keybindings": {
"copy": "تحديد النص",
"paste": "لصق",
"sendControlCode": "إرسال إشارة Ctrl+حرف",
"sendText": "أرسل رسالة نصية",
"controlCodeLabel": "خطاب",
"textLabel": "نص لإرساله",
"appendEnterLabel": "اضغط على زر الإدخال بعد الإرسال",
"clipboardPermissionNote": "قد يطلب منك بعض المتصفحات منح إذن الوصول إلى الحافظة عند الاستخدام الأول.",
"textRequired": "أدخل النص المراد إرساله",
"controlCodeRequired": "أدخل حرفًا واحدًا لرمز التحكم"
},
"macros": {
"title": "وحدات الماكرو",
"description": "قم بتشغيل سلسلة من الأوامر المحفوظة في طرفية، وتفاعل مع ما يرسله الخادم.",
"target": "هدف",
"noTerminal": "لا يوجد طرفية مفتوحة",
"empty": "لا توجد وحدات ماكرو حتى الآن",
"emptyHint": "أنشئ ماكرو لأتمتة تدفق طرفية، مثل تسجيل الدخول أو إعادة تشغيل خدمة أو الإجابة على المطالبات.",
"create": "ماكرو جديد",
"back": "العودة إلى وحدات الماكرو",
"name": "اسم",
"namePlaceholder": "أعد تشغيل خادم الويب",
"nameRequired": "قم بتسمية الماكرو قبل حفظه",
"descriptionLabel": "وصف",
"descriptionPlaceholder": "ما يفعله هذا الماكرو (اختياري)",
"unsaved": "التغييرات غير المحفوظة",
"save": "يحفظ",
"saved": "تم حفظ الماكرو",
"saveFailed": "تعذر حفظ الماكرو",
"delete": "يمسح",
"deleteTitle": "هل تريد حذف هذا الماكرو؟",
"deleteBody": "سيتم حذف \"{{name}}\" نهائياً. لا يمكن التراجع عن هذا الإجراء.",
"deleted": "تم حذف الماكرو",
"cancel": "يلغي",
"run": "يجري",
"stop": "قف",
"running": "جري...",
"completed": "اللمسة النهائية الدقيقة",
"failed": "فشل الماكرو",
"loadFailed": "تعذر تحميل وحدات الماكرو",
"needTerminal": "افتح وقم بتوصيل طرفية أولاً",
"noSteps": "لا يحتوي هذا الماكرو على أي خطوات حتى الآن. أضف خطوة واحدة أدناه.",
"addStep": "خطوات إضافية",
"stepNumber": "الخطوة {{number}}",
"removeStep": "خطوة الإزالة",
"confirmRunTitle": "هل تريد تشغيل هذا الماكرو؟",
"confirmRunBody": "\"{{name}}\" سيرسل أوامر إلى {{host}}.",
"confirmRunAction": "تشغيل الماكرو",
"stepTypes": {
"send": {
"label": "إرسال الأمر",
"help": "يكتب النص في الطرفية، تمامًا كما لو كنت أنت من كتبته."
},
"wait": {
"label": "انتظر حتى يتم إخراج البيانات",
"help": "يتوقف مؤقتًا حتى يقوم الخادم بطباعة النص الذي تحدده، مثل مطالبة كلمة المرور."
},
"delay": {
"label": "يوقف",
"help": "ينتظر فترة زمنية محددة قبل الخطوة التالية."
},
"if": {
"label": "إذا احتوى الناتج",
"help": "يقوم بفحص كل ما طبعه الخادم حتى الآن، ثم يقوم بتشغيل أحد الفرعين."
},
"repeat": {
"label": "يكرر",
"help": "يقوم بتنفيذ الخطوات الموجودة بداخله لعدد محدد من المرات."
}
},
"send": {
"text": "أمر أو نص",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "اضغط على زر الإدخال بعد الإرسال",
"pressEnterHelp": "قم بإيقاف تشغيل هذا الخيار لكتابة نص دون إرساله."
},
"wait": {
"pattern": "انتظر حتى يحتوي الناتج على",
"patternPlaceholder": "كلمة المرور:",
"timeout": "الاستسلام بعد",
"onTimeout": "إذا لم يظهر أبداً",
"stop": "أوقف الماكرو",
"continue": "استمر على أي حال"
},
"delay": {
"duration": "انتظر"
},
"ifStep": {
"pattern": "إذا احتوى الناتج",
"patternPlaceholder": "نشط: نشط (قيد التشغيل)",
"then": "ثم افعل هذا",
"else": "وإلا فافعل هذا",
"emptyBranch": "لا توجد"
},
"repeat": {
"count": "عدد المرات",
"times": "أوقات"
},
"match": {
"useRegex": "مطابقة مع تعبير نمطي",
"useRegexHelp": "اترك هذا الخيار فارغًا لمطابقة النص تمامًا كما هو مكتوب.",
"invalidRegex": "هذا ليس تعبيرًا نمطيًا صالحًا، وسيتم مطابقته كنص عادي."
},
"seconds": "ثوانٍ",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Запазени теми",
"scrollbackBufferLabel": "Буфер за превъртане назад",
"scrollbackMaxLines": "Максимален брой редове, съхранявани в историята",
"startupSnippetLabel": "Стартов фрагмент",
"sudoPasswordAutoFill": "Автоматично попълване на парола за Sudo",
"sudoPasswordAutoFillDesc": "Съхранявайте парола за sudo, така че показателите за хоста, подканите на терминала и други да могат да изпълняват привилегировани команди автоматично.",
"sudoPasswordAutoFillLabel": "Автоматично попълване на парола за Sudo",
@@ -335,7 +334,8 @@
"history": "История",
"localTerminal": "Местен терминал",
"sshTools": "SSH инструменти",
"terminal": "Терминал"
"terminal": "Терминал",
"macros": "Макроси"
},
"sshTools": {
"broadcastInputPlaceholder": "Въведете тук, за да излъчвате натисканията на клавиши...",
@@ -363,5 +363,112 @@
"off": "Изключено",
"on": "Включено",
"title": "Настройки на връзката по подразбиране"
},
"keybindings": {
"copy": "Копиране на селекцията",
"paste": "Паста",
"sendControlCode": "Изпрати сигнал Ctrl+буква",
"sendText": "Изпращане на текст",
"controlCodeLabel": "Писмо",
"textLabel": "Текст за изпращане",
"appendEnterLabel": "Натиснете Enter след изпращане",
"clipboardPermissionNote": "Може да поиска разрешение за достъп до клипборда при първото използване в някои браузъри.",
"textRequired": "Въведете текста за изпращане",
"controlCodeRequired": "Въведете една буква за контролния код"
},
"macros": {
"title": "Макроси",
"description": "Изпълнете запазена серия от команди в терминал и реагирайте на това, което сървърът изпраща обратно.",
"target": "Цел",
"noTerminal": "Няма отворен терминал",
"empty": "Все още няма макроси",
"emptyHint": "Създайте макрос за автоматизиране на процес в терминал, като например влизане в системата, рестартиране на услуга или отговаряне на подкани.",
"create": "Нов макрос",
"back": "Обратно към макросите",
"name": "Име",
"namePlaceholder": "Рестартирайте уеб сървъра",
"nameRequired": "Дайте име на макроса, преди да го запазите",
"descriptionLabel": "Описание",
"descriptionPlaceholder": "Какво прави този макрос (по избор)",
"unsaved": "Незапазени промени",
"save": "Запазване",
"saved": "Макросът е запазен",
"saveFailed": "Макросът не можа да се запази",
"delete": "Изтриване",
"deleteTitle": "Да се изтрие ли този макрос?",
"deleteBody": "„{{name}}“ ще бъде изтрито за постоянно. Това не може да бъде отменено.",
"deleted": "Макросът е изтрит",
"cancel": "Отказ",
"run": "Бягане",
"stop": "Спри",
"running": "Бягане...",
"completed": "Макрото е завършено",
"failed": "Макросът не беше успешен",
"loadFailed": "Не можаха да се заредят макроси",
"needTerminal": "Първо отворете и свържете терминал",
"noSteps": "Този макрос все още няма стъпки. Добавете една по-долу.",
"addStep": "Добавяне на стъпка",
"stepNumber": "Стъпка {{number}}",
"removeStep": "Премахване на стъпка",
"confirmRunTitle": "Да се изпълни ли този макрос?",
"confirmRunBody": "„{{name}}“ ще изпраща команди до {{host}}.",
"confirmRunAction": "Изпълнете макрос",
"stepTypes": {
"send": {
"label": "Изпрати команда",
"help": "Въвежда текст в терминала, по същия начин, както ако сте го написали."
},
"wait": {
"label": "Изчакайте изхода",
"help": "Пауза, докато сървърът отпечата текста, който посочите, като например подкана за парола."
},
"delay": {
"label": "Пауза",
"help": "Изчаква фиксирано време преди следващата стъпка."
},
"if": {
"label": "Ако изходът съдържа",
"help": "Проверява всичко, което сървърът е отпечатал досега, след което изпълнява един от двата клона."
},
"repeat": {
"label": "Повторете",
"help": "Изпълнява стъпките вътре в него определен брой пъти."
}
},
"send": {
"text": "Команда или текст",
"textPlaceholder": "systemctl рестартиране на nginx",
"pressEnter": "Натиснете Enter след изпращане",
"pressEnterHelp": "Изключете това, за да пишете текст, без да го изпращате."
},
"wait": {
"pattern": "Изчакайте, докато изходът съдържа",
"patternPlaceholder": "парола:",
"timeout": "Откажи се след",
"onTimeout": "Ако никога не се появи",
"stop": "Спиране на макроса",
"continue": "Продължавай все пак"
},
"delay": {
"duration": "Изчакайте"
},
"ifStep": {
"pattern": "Ако изходът съдържа",
"patternPlaceholder": "Активен: активен (работи)",
"then": "Тогава направете това",
"else": "В противен случай направете това",
"emptyBranch": "Без стъпала"
},
"repeat": {
"count": "Брой пъти",
"times": "пъти"
},
"match": {
"useRegex": "Съвпадение с регулярен израз",
"useRegexHelp": "Не включвайте това, за да съвпада текстът точно както е написан.",
"invalidRegex": "Не е валиден регулярен израз, ще бъде съпоставен като обикновен текст"
},
"seconds": "секунди",
"secondsShort": "{{count}}с"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "সংরক্ষিত থিম",
"scrollbackBufferLabel": "স্ক্রোলব্যাক বাফার",
"scrollbackMaxLines": "ইতিহাসে সংরক্ষিত সর্বাধিক সংখ্যক লাইন",
"startupSnippetLabel": "স্টার্টআপ স্নিপেট",
"sudoPasswordAutoFill": "সুডো পাসওয়ার্ড স্বয়ংক্রিয়ভাবে পূরণ",
"sudoPasswordAutoFillDesc": "একটি sudo পাসওয়ার্ড সংরক্ষণ করুন যাতে হোস্ট মেট্রিক্স, টার্মিনাল প্রম্পট এবং অন্যান্যরা স্বয়ংক্রিয়ভাবে বিশেষাধিকারপ্রাপ্ত কমান্ড চালাতে পারে।",
"sudoPasswordAutoFillLabel": "সুডো পাসওয়ার্ড স্বয়ংক্রিয়ভাবে পূরণ",
@@ -335,7 +334,8 @@
"history": "ইতিহাস",
"localTerminal": "স্থানীয় টার্মিনাল",
"sshTools": "SSH টুলস",
"terminal": "টার্মিনাল"
"terminal": "টার্মিনাল",
"macros": "ম্যাক্রো"
},
"sshTools": {
"broadcastInputPlaceholder": "কীস্ট্রোক সম্প্রচার করতে এখানে টাইপ করুন...",
@@ -363,5 +363,112 @@
"off": "বন্ধ",
"on": "চালু",
"title": "সংযোগের ডিফল্ট"
},
"keybindings": {
"copy": "কপি নির্বাচন",
"paste": "পেস্ট",
"sendControlCode": "Ctrl+অক্ষর সংকেত পাঠান",
"sendText": "টেক্সট পাঠান",
"controlCodeLabel": "চিঠি",
"textLabel": "পাঠানোর জন্য টেক্সট",
"appendEnterLabel": "পাঠানোর পর এন্টার চাপুন",
"clipboardPermissionNote": "কিছু ব্রাউজারে প্রথমবার ব্যবহারের সময় ক্লিপবোর্ডের অনুমতি চাইতে পারে।",
"textRequired": "পাঠানোর জন্য টেক্সটটি লিখুন।",
"controlCodeRequired": "কন্ট্রোল কোডের জন্য একটি অক্ষর লিখুন।"
},
"macros": {
"title": "ম্যাক্রো",
"description": "টার্মিনালে সংরক্ষিত কমান্ডের একটি সিরিজ চালান এবং সার্ভার যা ফেরত পাঠায় তার উপর প্রতিক্রিয়া জানান।",
"target": "লক্ষ্য",
"noTerminal": "কোন টার্মিনাল খোলা নেই",
"empty": "এখনো কোনো ম্যাক্রো নেই",
"emptyHint": "টার্মিনালের কোনো কাজ, যেমন লগ ইন করা, কোনো সার্ভিস রিস্টার্ট করা বা প্রম্পটের উত্তর দেওয়া, স্বয়ংক্রিয় করতে একটি ম্যাক্রো তৈরি করুন।",
"create": "নতুন ম্যাক্রো",
"back": "ম্যাক্রোতে ফিরে যান",
"name": "নাম",
"namePlaceholder": "ওয়েব সার্ভার পুনরায় চালু করুন",
"nameRequired": "সংরক্ষণ করার আগে ম্যাক্রোটির একটি নাম দিন।",
"descriptionLabel": "বর্ণনা",
"descriptionPlaceholder": "এই ম্যাক্রোটি কী করে (ঐচ্ছিক)",
"unsaved": "সংরক্ষিত নয় এমন পরিবর্তন",
"save": "সংরক্ষণ করুন",
"saved": "ম্যাক্রো সংরক্ষিত",
"saveFailed": "ম্যাক্রোটি সংরক্ষণ করা যায়নি।",
"delete": "মুছে ফেলুন",
"deleteTitle": "এই ম্যাক্রোটি মুছে ফেলবেন?",
"deleteBody": "\"{{name}}\" স্থায়ীভাবে মুছে ফেলা হবে। এটি আর পূর্বাবস্থায় ফেরানো যাবে না।",
"deleted": "ম্যাক্রো মুছে ফেলা হয়েছে",
"cancel": "বাতিল করুন",
"run": "দৌড়",
"stop": "থামুন",
"running": "দৌড়াচ্ছে...",
"completed": "ম্যাক্রো শেষ হয়েছে",
"failed": "ম্যাক্রো ব্যর্থ হয়েছে",
"loadFailed": "ম্যাক্রো লোড করা যায়নি",
"needTerminal": "প্রথমে একটি টার্মিনাল খুলুন এবং সংযোগ করুন।",
"noSteps": "এই ম্যাক্রোতে এখনও কোনো ধাপ নেই। নিচে একটি যোগ করুন।",
"addStep": "ধাপ যোগ করুন",
"stepNumber": "ধাপ {{number}}",
"removeStep": "ধাপটি সরান",
"confirmRunTitle": "এই ম্যাক্রোটি চালাবেন?",
"confirmRunBody": "\"{{name}}\" {{host}} -এ কমান্ড পাঠাবে।",
"confirmRunAction": "ম্যাক্রো চালান",
"stepTypes": {
"send": {
"label": "কমান্ড পাঠান",
"help": "টার্মিনালে টেক্সট টাইপ করে, ঠিক যেন আপনি নিজেই টাইপ করেছেন।"
},
"wait": {
"label": "আউটপুটের জন্য অপেক্ষা করুন",
"help": "সার্ভার আপনার দেওয়া টেক্সটটি প্রিন্ট না করা পর্যন্ত এটি থেমে থাকে, অনেকটা পাসওয়ার্ড দেওয়ার সময়কার মতো।"
},
"delay": {
"label": "বিরতি",
"help": "পরবর্তী ধাপে যাওয়ার আগে একটি নির্দিষ্ট পরিমাণ সময় অপেক্ষা করে।"
},
"if": {
"label": "যদি আউটপুটে থাকে",
"help": "সার্ভার এ পর্যন্ত যা যা প্রিন্ট করেছে, তা যাচাই করে দুটি শাখার মধ্যে একটি চালায়।"
},
"repeat": {
"label": "পুনরাবৃত্তি",
"help": "এর ভেতরের ধাপগুলো একটি নির্দিষ্ট সংখ্যক বার চালায়।"
}
},
"send": {
"text": "কমান্ড বা টেক্সট",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "পাঠানোর পর এন্টার চাপুন",
"pressEnterHelp": "সাবমিট না করে টেক্সট টাইপ করতে এটি বন্ধ করুন।"
},
"wait": {
"pattern": "আউটপুটে থাকা পর্যন্ত অপেক্ষা করুন",
"patternPlaceholder": "পাসওয়ার্ড:",
"timeout": "হাল ছেড়ে দেওয়ার পর",
"onTimeout": "যদি এটি কখনও প্রদর্শিত না হয়",
"stop": "ম্যাক্রোটি বন্ধ করুন",
"continue": "তবুও এগিয়ে যান।"
},
"delay": {
"duration": "অপেক্ষা করুন"
},
"ifStep": {
"pattern": "যদি আউটপুটে থাকে",
"patternPlaceholder": "সক্রিয়: সক্রিয় (চলমান)",
"then": "তারপর এটা করুন",
"else": "অন্যথায় এটা করুন",
"emptyBranch": "কোন পদক্ষেপ নেই"
},
"repeat": {
"count": "সংখ্যা",
"times": "সময়"
},
"match": {
"useRegex": "রেগুলার এক্সপ্রেশনের সাথে মেলান",
"useRegexHelp": "লেখাটির সাথে হুবহু মিল রাখতে এটি বাদ দিন।",
"invalidRegex": "এটি একটি বৈধ রেগুলার এক্সপ্রেশন নয়, এটিকে সাধারণ টেক্সট হিসেবে মেলানো হবে।"
},
"seconds": "সেকেন্ড",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temes desats",
"scrollbackBufferLabel": "Buffer de desplaçament enrere",
"scrollbackMaxLines": "Nombre màxim de línies que es conserven a l'historial",
"startupSnippetLabel": "Fragment d'inici",
"sudoPasswordAutoFill": "Emplenament automàtic de contrasenya de Sudo",
"sudoPasswordAutoFillDesc": "Emmagatzema una contrasenya sudo perquè les mètriques de l'amfitrió, les indicacions del terminal i altres puguin executar ordres privilegiades automàticament.",
"sudoPasswordAutoFillLabel": "Autoemplenament de contrasenya de Sudo",
@@ -335,7 +334,8 @@
"history": "Història",
"localTerminal": "Terminal local",
"sshTools": "Eines SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Escriu aquí per emetre les pulsacions de tecles...",
@@ -363,5 +363,112 @@
"off": "Desactivat",
"on": "Activat",
"title": "Valors predeterminats de connexió"
},
"keybindings": {
"copy": "Copia la selecció",
"paste": "Enganxa",
"sendControlCode": "Envia el senyal Ctrl+lletra",
"sendText": "Enviar text",
"controlCodeLabel": "Carta",
"textLabel": "Text per enviar",
"appendEnterLabel": "Premeu Intro després d'enviar",
"clipboardPermissionNote": "Pot demanar permisos al porta-retalls en el primer ús en alguns navegadors.",
"textRequired": "Introduïu el text per enviar",
"controlCodeRequired": "Introduïu una sola lletra per al codi de control"
},
"macros": {
"title": "Macros",
"description": "Executa una sèrie d'ordres desades en un terminal i reacciona al que el servidor envia de tornada.",
"target": "Objectiu",
"noTerminal": "Cap terminal obert",
"empty": "Encara no hi ha macros",
"emptyHint": "Crea una macro per automatitzar un flux de terminal, com ara iniciar la sessió, reiniciar un servei o respondre a les indicacions.",
"create": "Nova macro",
"back": "Torna a les macros",
"name": "Nom",
"namePlaceholder": "Reinicia el servidor web",
"nameRequired": "Doneu un nom a la macro abans de desar-la",
"descriptionLabel": "Descripció",
"descriptionPlaceholder": "Què fa aquesta macro (opcional)",
"unsaved": "Canvis no desats",
"save": "Desa",
"saved": "Macro desada",
"saveFailed": "No s'ha pogut desar la macro",
"delete": "Suprimeix",
"deleteTitle": "Voleu suprimir aquesta macro?",
"deleteBody": "«{{name}}» s'eliminarà permanentment. Això no es pot desfer.",
"deleted": "Macro suprimida",
"cancel": "Cancel·la",
"run": "Corre",
"stop": "Atura",
"running": "Corrent...",
"completed": "Macro acabada",
"failed": "La macro ha fallat",
"loadFailed": "No s'han pogut carregar les macros",
"needTerminal": "Obriu i connecteu primer un terminal",
"noSteps": "Aquesta macro encara no té cap pas. Afegeix-ne un a continuació.",
"addStep": "Afegeix un pas",
"stepNumber": "Pas {{number}}",
"removeStep": "Elimina el pas",
"confirmRunTitle": "Voleu executar aquesta macro?",
"confirmRunBody": "\"{{name}}\" enviarà ordres a {{host}}.",
"confirmRunAction": "Executa macro",
"stepTypes": {
"send": {
"label": "Envia l'ordre",
"help": "Escriu text al terminal, igual que si l'haguessis escrit."
},
"wait": {
"label": "Espera la sortida",
"help": "Es posa en pausa fins que el servidor imprimeix el text que anomeneu, com si fos una sol·licitud de contrasenya."
},
"delay": {
"label": "Pausa",
"help": "Espera un temps fix abans del següent pas."
},
"if": {
"label": "Si la sortida conté",
"help": "Comprova tot el que el servidor ha imprès fins ara i després executa una de les dues branques."
},
"repeat": {
"label": "Repetir",
"help": "Executa els passos que hi ha dins un nombre determinat de vegades."
}
},
"send": {
"text": "Ordre o text",
"textPlaceholder": "reinici de systemctl a nginx",
"pressEnter": "Premeu Intro després d'enviar",
"pressEnterHelp": "Desactiva això per escriure text sense enviar-lo."
},
"wait": {
"pattern": "Espereu fins que la sortida contingui",
"patternPlaceholder": "contrasenya:",
"timeout": "Renunciar després",
"onTimeout": "Si mai apareix",
"stop": "Atura la macro",
"continue": "Continua igualment"
},
"delay": {
"duration": "Espera"
},
"ifStep": {
"pattern": "Si la sortida conté",
"patternPlaceholder": "Actiu: actiu (en funcionament)",
"then": "Aleshores fes això",
"else": "Si no, fes això",
"emptyBranch": "Sense esglaons"
},
"repeat": {
"count": "Nombre de vegades",
"times": "vegades"
},
"match": {
"useRegex": "Coincideix amb una expressió regular",
"useRegexHelp": "Deixa això desactivat perquè el text coincideixi exactament com està escrit.",
"invalidRegex": "No és una expressió regular vàlida, es trobarà com a text sense format."
},
"seconds": "segons",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Uložená témata",
"scrollbackBufferLabel": "Vyrovnávací paměť pro rolování zpět",
"scrollbackMaxLines": "Maximální počet řádků uchovávaných v historii",
"startupSnippetLabel": "Úryvek pro spuštění",
"sudoPasswordAutoFill": "Automatické vyplňování hesla Sudo",
"sudoPasswordAutoFillDesc": "Uložte heslo sudo, aby metriky hostitele, výzvy terminálu a další mohly automaticky spouštět privilegované příkazy.",
"sudoPasswordAutoFillLabel": "Automatické vyplňování hesla Sudo",
@@ -335,7 +334,8 @@
"history": "Dějiny",
"localTerminal": "Místní terminál",
"sshTools": "SSH nástroje",
"terminal": "Terminál"
"terminal": "Terminál",
"macros": "Makra"
},
"sshTools": {
"broadcastInputPlaceholder": "Zde napište pro vysílání stisknutí kláves...",
@@ -363,5 +363,112 @@
"off": "Vypnuto",
"on": "Na",
"title": "Výchozí nastavení připojení"
},
"keybindings": {
"copy": "Kopírovat výběr",
"paste": "Pasta",
"sendControlCode": "Odeslat signál Ctrl+písmeno",
"sendText": "Odeslat text",
"controlCodeLabel": "Dopis",
"textLabel": "Text k odeslání",
"appendEnterLabel": "Po odeslání stiskněte Enter",
"clipboardPermissionNote": "V některých prohlížečích se může při prvním použití zobrazit výzva k zadání povolení ke schránce.",
"textRequired": "Zadejte text k odeslání",
"controlCodeRequired": "Zadejte jedno písmeno pro kontrolní kód"
},
"macros": {
"title": "Makra",
"description": "Spusťte v terminálu uloženou sérii příkazů a reagujte na to, co server odešle zpět.",
"target": "Cíl",
"noTerminal": "Žádný otevřený terminál",
"empty": "Zatím žádná makra",
"emptyHint": "Vytvořte makro pro automatizaci procesů v terminálu, jako je přihlašování, restartování služby nebo odpovídání na výzvy.",
"create": "Nové makro",
"back": "Zpět k makrům",
"name": "Jméno",
"namePlaceholder": "Restartujte webový server",
"nameRequired": "Před uložením zadejte makro název",
"descriptionLabel": "Popis",
"descriptionPlaceholder": "Co toto makro dělá (volitelné)",
"unsaved": "Neuložené změny",
"save": "Uložit",
"saved": "Makro uloženo",
"saveFailed": "Makro se nepodařilo uložit.",
"delete": "Vymazat",
"deleteTitle": "Smazat toto makro?",
"deleteBody": "Soubor „{{name}}“ bude trvale smazán. Tuto akci nelze vrátit zpět.",
"deleted": "Makro smazáno",
"cancel": "Zrušit",
"run": "Běh",
"stop": "Zastávka",
"running": "Běh...",
"completed": "Makro dokončeno",
"failed": "Makro selhalo",
"loadFailed": "Nepodařilo se načíst makra",
"needTerminal": "Nejprve otevřete a připojte terminál",
"noSteps": "Toto makro zatím nemá žádné kroky. Přidejte jeden níže.",
"addStep": "Přidat krok",
"stepNumber": "Krok {{number}}",
"removeStep": "Odebrat krok",
"confirmRunTitle": "Spustit toto makro?",
"confirmRunBody": "„{{name}}“ odešle příkazy pro {{host}}.",
"confirmRunAction": "Spustit makro",
"stepTypes": {
"send": {
"label": "Odeslat příkaz",
"help": "Zadá text do terminálu, stejně jako byste ho napsali."
},
"wait": {
"label": "Počkejte na výstup",
"help": "Pozastaví se, dokud server nevypíše text, který pojmenujete, například výzvu k zadání hesla."
},
"delay": {
"label": "Pauza",
"help": "Čeká pevně stanovenou dobu před dalším krokem."
},
"if": {
"label": "Pokud výstup obsahuje",
"help": "Zkontroluje vše, co server dosud vytiskl, a poté spustí jednu ze dvou větví."
},
"repeat": {
"label": "Opakovat",
"help": "Spustí kroky uvnitř něj tolikrát, kolikrát je nastaveno."
}
},
"send": {
"text": "Příkaz nebo text",
"textPlaceholder": "restartování systemctl v Nginxu",
"pressEnter": "Po odeslání stiskněte Enter",
"pressEnterHelp": "Vypněte tuto funkci, chcete-li psát text bez jeho odesílání."
},
"wait": {
"pattern": "Počkejte, dokud výstup neobsahuje",
"patternPlaceholder": "heslo:",
"timeout": "Vzdej se po",
"onTimeout": "Pokud se to nikdy neobjeví",
"stop": "Zastavit makro",
"continue": "Stejně pokračuj"
},
"delay": {
"duration": "Čekat na"
},
"ifStep": {
"pattern": "Pokud výstup obsahuje",
"patternPlaceholder": "Aktivní: aktivní (běží)",
"then": "Pak udělej tohle",
"else": "Jinak to udělej",
"emptyBranch": "Žádné schody"
},
"repeat": {
"count": "Počet opakování",
"times": "krát"
},
"match": {
"useRegex": "Porovnání s regulárním výrazem",
"useRegexHelp": "Nechte toto vypnuté, aby text přesně odpovídal napsanému textu.",
"invalidRegex": "Není to platný regulární výraz, bude nalezen jako prostý text."
},
"seconds": "sekundy",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Gemte temaer",
"scrollbackBufferLabel": "Scrollback-buffer",
"scrollbackMaxLines": "Maksimalt antal linjer gemt i historikken",
"startupSnippetLabel": "Opstartskodestykke",
"sudoPasswordAutoFill": "Sudo-adgangskode automatisk udfyldning",
"sudoPasswordAutoFillDesc": "Gem en sudo-adgangskode, så værtsmålinger, terminalprompter og andre kan køre privilegerede kommandoer automatisk.",
"sudoPasswordAutoFillLabel": "Sudo-adgangskode automatisk udfyldning",
@@ -335,7 +334,8 @@
"history": "Historie",
"localTerminal": "Lokal terminal",
"sshTools": "SSH-værktøjer",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makroer"
},
"sshTools": {
"broadcastInputPlaceholder": "Skriv her for at udsende tastetryk...",
@@ -363,5 +363,112 @@
"off": "Slukket",
"on": "På",
"title": "Standardindstillinger for forbindelse"
},
"keybindings": {
"copy": "Kopiér valg",
"paste": "Indsæt",
"sendControlCode": "Send Ctrl+bogstavsignal",
"sendText": "Send sms",
"controlCodeLabel": "Bogstav",
"textLabel": "Tekst der skal sendes",
"appendEnterLabel": "Tryk Enter efter afsendelse",
"clipboardPermissionNote": "Kan bede om tilladelse til udklipsholder ved første brug i nogle browsere.",
"textRequired": "Indtast den tekst, der skal sendes",
"controlCodeRequired": "Indtast et enkelt bogstav for kontrolkoden"
},
"macros": {
"title": "Makroer",
"description": "Kør en gemt række kommandoer i en terminal, og reager på, hvad serveren sender tilbage.",
"target": "Mål",
"noTerminal": "Ingen terminal åben",
"empty": "Ingen makroer endnu",
"emptyHint": "Opret en makro for at automatisere et terminalflow, f.eks. at logge ind, genstarte en tjeneste eller besvare prompts.",
"create": "Ny makro",
"back": "Tilbage til makroer",
"name": "Navn",
"namePlaceholder": "Genstart webserveren",
"nameRequired": "Giv makroen et navn, før du gemmer den",
"descriptionLabel": "Beskrivelse",
"descriptionPlaceholder": "Hvad denne makro gør (valgfrit)",
"unsaved": "Ikke-gemte ændringer",
"save": "Spare",
"saved": "Makro gemt",
"saveFailed": "Makroen kunne ikke gemmes",
"delete": "Slet",
"deleteTitle": "Slette denne makro?",
"deleteBody": "\"{{name}}\" vil blive slettet permanent. Dette kan ikke fortrydes.",
"deleted": "Makro slettet",
"cancel": "Ophæve",
"run": "Løbe",
"stop": "Stop",
"running": "Løber...",
"completed": "Makro færdig",
"failed": "Makro mislykkedes",
"loadFailed": "Makroer kunne ikke indlæses",
"needTerminal": "Åbn og tilslut først en terminal",
"noSteps": "Denne makro har endnu ingen trin. Tilføj et nedenfor.",
"addStep": "Tilføj trin",
"stepNumber": "Trin {{number}}",
"removeStep": "Fjern trin",
"confirmRunTitle": "Køre denne makro?",
"confirmRunBody": "\"{{name}}\" sender kommandoer til {{host}}.",
"confirmRunAction": "Kør makro",
"stepTypes": {
"send": {
"label": "Send kommando",
"help": "Skriver tekst i terminalen, på samme måde som hvis du havde skrevet den."
},
"wait": {
"label": "Vent på output",
"help": "Holder pause, indtil serveren udskriver den tekst, du navngiver, f.eks. en adgangskodeprompt."
},
"delay": {
"label": "Pause",
"help": "Venter et fast tidsrum før næste trin."
},
"if": {
"label": "Hvis outputtet indeholder",
"help": "Kontrollerer alt, hvad serveren har udskrevet indtil videre, og kører derefter en af to grene."
},
"repeat": {
"label": "Gentage",
"help": "Kører trinnene inde i den et bestemt antal gange."
}
},
"send": {
"text": "Kommando eller tekst",
"textPlaceholder": "systemctl genstart nginx",
"pressEnter": "Tryk Enter efter afsendelse",
"pressEnterHelp": "Slå dette fra for at skrive tekst uden at indsende den."
},
"wait": {
"pattern": "Vent indtil outputtet indeholder",
"patternPlaceholder": "adgangskode:",
"timeout": "Giv op efter",
"onTimeout": "Hvis det aldrig dukker op",
"stop": "Stop makroen",
"continue": "Fortsæt alligevel"
},
"delay": {
"duration": "Vent på"
},
"ifStep": {
"pattern": "Hvis outputtet indeholder",
"patternPlaceholder": "Aktiv: aktiv (løber)",
"then": "Så gør dette",
"else": "Ellers gør dette",
"emptyBranch": "Ingen trin"
},
"repeat": {
"count": "Antal gange",
"times": "gange"
},
"match": {
"useRegex": "Match med et regulært udtryk",
"useRegexHelp": "Slå dette fra for at teksten skal være præcis som den er skrevet.",
"invalidRegex": "Ikke et gyldigt regulært udtryk, det vil blive matchet som almindelig tekst"
},
"seconds": "sekunder",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Gespeicherte Designs",
"scrollbackBufferLabel": "Rückrollpuffer",
"scrollbackMaxLines": "Maximale Anzahl der im Verlauf gespeicherten Zeilen",
"startupSnippetLabel": "Startup-Snippet",
"sudoPasswordAutoFill": "Sudo-Passwort automatisch ausfüllen",
"sudoPasswordAutoFillDesc": "Sudo-Passwort speichern, damit Hostmetriken, Terminal-Eingabeaufforderungen und andere privilegierte Befehle automatisch ausgeführt werden können.",
"sudoPasswordAutoFillLabel": "Sudo-Passwort automatisch ausfüllen",
@@ -335,7 +334,8 @@
"history": "Verlauf",
"localTerminal": "Lokales Terminal",
"sshTools": "SSH-Tools",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makros"
},
"sshTools": {
"broadcastInputPlaceholder": "Hier tippen, um Tastenanschläge zu senden...",
@@ -363,5 +363,112 @@
"off": "Aus",
"on": "An",
"title": "Standardeinstellungen für die Verbindung"
},
"keybindings": {
"copy": "Auswahl kopieren",
"paste": "Paste",
"sendControlCode": "Sende das Signal Strg+Buchstabe",
"sendText": "SMS senden",
"controlCodeLabel": "Brief",
"textLabel": "Text zum Senden",
"appendEnterLabel": "Nach dem Absenden die Eingabetaste drücken",
"clipboardPermissionNote": "Bei einigen Browsern kann es bei der ersten Verwendung zu einer Anfrage nach der Berechtigung zum Ablegen der Zwischenablage kommen.",
"textRequired": "Geben Sie den zu sendenden Text ein.",
"controlCodeRequired": "Geben Sie einen einzelnen Buchstaben als Kontrollcode ein."
},
"macros": {
"title": "Makros",
"description": "Führe eine gespeicherte Befehlsfolge in einem Terminal aus und reagiere auf die Antwort des Servers.",
"target": "Ziel",
"noTerminal": "Kein Terminal geöffnet",
"empty": "Noch keine Makros",
"emptyHint": "Erstellen Sie ein Makro, um einen Terminal-Workflow zu automatisieren, z. B. das Anmelden, das Neustarten eines Dienstes oder das Beantworten von Eingabeaufforderungen.",
"create": "Neues Makro",
"back": "Zurück zu den Makros",
"name": "Name",
"namePlaceholder": "Webserver neu starten",
"nameRequired": "Gib dem Makro vor dem Speichern einen Namen.",
"descriptionLabel": "Beschreibung",
"descriptionPlaceholder": "Was dieses Makro bewirkt (optional)",
"unsaved": "Nicht gespeicherte Änderungen",
"save": "Speichern",
"saved": "Makro gespeichert",
"saveFailed": "Makro konnte nicht gespeichert werden",
"delete": "Löschen",
"deleteTitle": "Dieses Makro löschen?",
"deleteBody": "\"{{name}}\" wird endgültig gelöscht. Dies kann nicht rückgängig gemacht werden.",
"deleted": "Makro gelöscht",
"cancel": "Stornieren",
"run": "Laufen",
"stop": "Stoppen",
"running": "Läuft...",
"completed": "Makro beendet",
"failed": "Makro fehlgeschlagen",
"loadFailed": "Makros konnten nicht geladen werden.",
"needTerminal": "Öffnen und verbinden Sie zuerst ein Terminal.",
"noSteps": "Dieses Makro hat noch keine Schritte. Füge unten einen hinzu.",
"addStep": "Schritt hinzufügen",
"stepNumber": "Schritt {{number}}",
"removeStep": "Schritt entfernen",
"confirmRunTitle": "Dieses Makro ausführen?",
"confirmRunBody": "\"{{name}}\" sendet Befehle an {{host}}.",
"confirmRunAction": "Makro ausführen",
"stepTypes": {
"send": {
"label": "Befehl senden",
"help": "Gibt den Text im Terminal ein, genau so, als ob Sie ihn eingetippt hätten."
},
"wait": {
"label": "Auf Ausgabe warten",
"help": "Die Ausgabe wird so lange unterbrochen, bis der Server den von Ihnen angegebenen Text ausgibt, beispielsweise eine Passwortabfrage."
},
"delay": {
"label": "Pause",
"help": "Wartet eine festgelegte Zeitspanne, bevor der nächste Schritt ausgeführt wird."
},
"if": {
"label": "Wenn die Ausgabe enthält",
"help": "Prüft alles, was der Server bisher ausgegeben hat, und führt dann einen von zwei Zweigen aus."
},
"repeat": {
"label": "Wiederholen",
"help": "Führt die darin enthaltenen Schritte eine festgelegte Anzahl von Malen aus."
}
},
"send": {
"text": "Befehl oder Text",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Nach dem Absenden die Eingabetaste drücken",
"pressEnterHelp": "Deaktivieren Sie diese Option, um Text einzugeben, ohne ihn abzusenden."
},
"wait": {
"pattern": "Warten Sie, bis die Ausgabe Folgendes enthält",
"patternPlaceholder": "Passwort:",
"timeout": "Aufgeben nach",
"onTimeout": "Wenn es nie erscheint",
"stop": "Stoppe das Makro",
"continue": "Mach trotzdem weiter."
},
"delay": {
"duration": "Warten Sie auf"
},
"ifStep": {
"pattern": "Wenn die Ausgabe enthält",
"patternPlaceholder": "Aktiv: aktiv (läuft)",
"then": "Dann tu Folgendes:",
"else": "Andernfalls tun Sie Folgendes:",
"emptyBranch": "Keine Stufen"
},
"repeat": {
"count": "Anzahl der Male",
"times": "mal"
},
"match": {
"useRegex": "Übereinstimmung mit einem regulären Ausdruck",
"useRegexHelp": "Lassen Sie dies weg, damit der Text genau so bleibt, wie er geschrieben steht.",
"invalidRegex": "Kein gültiger regulärer Ausdruck, wird als Klartext erkannt."
},
"seconds": "Sekunden",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Αποθηκευμένα Θέματα",
"scrollbackBufferLabel": "Ενδιάμεσο buffer κύλισης προς τα πίσω",
"scrollbackMaxLines": "Μέγιστος αριθμός γραμμών που διατηρούνται στο ιστορικό",
"startupSnippetLabel": "Απόσπασμα εκκίνησης",
"sudoPasswordAutoFill": "Αυτόματη συμπλήρωση κωδικού πρόσβασης Sudo",
"sudoPasswordAutoFillDesc": "Αποθηκεύστε έναν κωδικό πρόσβασης sudo, ώστε οι μετρήσεις του κεντρικού υπολογιστή, οι προτροπές του τερματικού και άλλα να μπορούν να εκτελούν αυτόματα προνομιακές εντολές.",
"sudoPasswordAutoFillLabel": "Αυτόματη συμπλήρωση κωδικού πρόσβασης Sudo",
@@ -335,7 +334,8 @@
"history": "Ιστορία",
"localTerminal": "Τοπικό τερματικό",
"sshTools": "Εργαλεία SSH",
"terminal": "Τερματικό"
"terminal": "Τερματικό",
"macros": "Μακροεντολές"
},
"sshTools": {
"broadcastInputPlaceholder": "Πληκτρολογήστε εδώ για να μεταδώσετε πληκτρολογήσεις...",
@@ -363,5 +363,112 @@
"off": "Μακριά από",
"on": "Επί",
"title": "Προεπιλογές σύνδεσης"
},
"keybindings": {
"copy": "Αντιγραφή επιλογής",
"paste": "Πάστα",
"sendControlCode": "Αποστολή σήματος Ctrl+γράμμα",
"sendText": "Αποστολή κειμένου",
"controlCodeLabel": "Επιστολή",
"textLabel": "Μήνυμα προς αποστολή",
"appendEnterLabel": "Πατήστε Enter μετά την αποστολή",
"clipboardPermissionNote": "Ενδέχεται να ζητηθεί άδεια για το πρόχειρο κατά την πρώτη χρήση σε ορισμένα προγράμματα περιήγησης.",
"textRequired": "Εισαγάγετε το κείμενο που θέλετε να στείλετε",
"controlCodeRequired": "Εισαγάγετε ένα μόνο γράμμα για τον κωδικό ελέγχου"
},
"macros": {
"title": "Μακροεντολές",
"description": "Εκτελέστε μια αποθηκευμένη σειρά εντολών σε ένα τερματικό και αντιδράστε σε ό,τι στέλνει πίσω ο διακομιστής.",
"target": "Στόχος",
"noTerminal": "Δεν υπάρχει ανοιχτό τερματικό",
"empty": "Δεν υπάρχουν ακόμη μακροεντολές",
"emptyHint": "Δημιουργήστε μια μακροεντολή για να αυτοματοποιήσετε μια ροή τερματικού, όπως σύνδεση, επανεκκίνηση μιας υπηρεσίας ή απάντηση σε μηνύματα.",
"create": "Νέα μακροεντολή",
"back": "Επιστροφή στις μακροεντολές",
"name": "Ονομα",
"namePlaceholder": "Επανεκκίνηση διακομιστή ιστού",
"nameRequired": "Δώστε ένα όνομα στη μακροεντολή πριν από την αποθήκευση",
"descriptionLabel": "Περιγραφή",
"descriptionPlaceholder": "Τι κάνει αυτή η μακροεντολή (προαιρετικό)",
"unsaved": "Μη αποθηκευμένες αλλαγές",
"save": "Εκτός",
"saved": "Η μακροεντολή αποθηκεύτηκε",
"saveFailed": "Δεν ήταν δυνατή η αποθήκευση της μακροεντολής",
"delete": "Διαγράφω",
"deleteTitle": "Διαγραφή αυτής της μακροεντολής;",
"deleteBody": "Το \"{{name}}\" θα διαγραφεί οριστικά. Δεν είναι δυνατή η αναίρεση αυτής της ενέργειας.",
"deleted": "Η μακροεντολή διαγράφηκε",
"cancel": "Ματαίωση",
"run": "Τρέξιμο",
"stop": "Στάση",
"running": "Τρέξιμο...",
"completed": "Η μακροεντολή ολοκληρώθηκε",
"failed": "Η μακροεντολή απέτυχε.",
"loadFailed": "Δεν ήταν δυνατή η φόρτωση μακροεντολών",
"needTerminal": "Ανοίξτε και συνδέστε πρώτα ένα τερματικό",
"noSteps": "Αυτή η μακροεντολή δεν έχει ακόμη βήματα. Προσθέστε ένα παρακάτω.",
"addStep": "Προσθήκη βήματος",
"stepNumber": "Βήμα {{number}}",
"removeStep": "Αφαίρεση βήματος",
"confirmRunTitle": "Εκτέλεση αυτής της μακροεντολής;",
"confirmRunBody": "Η εντολή \"{{name}}\" θα στείλει εντολές στον χρήστη {{host}}.",
"confirmRunAction": "Εκτέλεση μακροεντολής",
"stepTypes": {
"send": {
"label": "Αποστολή εντολής",
"help": "Πληκτρολογεί κείμενο στο τερματικό, όπως θα το γράφατε."
},
"wait": {
"label": "Περιμένετε για έξοδο",
"help": "Παύει μέχρι ο διακομιστής να εκτυπώσει το κείμενο που ονομάζετε, σαν να σας ζητείται κωδικός πρόσβασης."
},
"delay": {
"label": "Παύση",
"help": "Περιμένει ένα συγκεκριμένο χρονικό διάστημα πριν από το επόμενο βήμα."
},
"if": {
"label": "Εάν η έξοδος περιέχει",
"help": "Ελέγχει όλα όσα έχει εκτυπώσει μέχρι στιγμής ο διακομιστής και, στη συνέχεια, εκτελεί μία από τις δύο διακλαδώσεις."
},
"repeat": {
"label": "Επαναλαμβάνω",
"help": "Εκτελεί τα βήματα στο εσωτερικό του έναν καθορισμένο αριθμό φορών."
}
},
"send": {
"text": "Εντολή ή κείμενο",
"textPlaceholder": "επανεκκίνηση systemctl nginx",
"pressEnter": "Πατήστε Enter μετά την αποστολή",
"pressEnterHelp": "Απενεργοποιήστε αυτήν τη λειτουργία για να πληκτρολογείτε κείμενο χωρίς να το υποβάλλετε."
},
"wait": {
"pattern": "Περιμένετε μέχρι η έξοδος να περιέχει",
"patternPlaceholder": "σύνθημα:",
"timeout": "Παραιτήσου μετά",
"onTimeout": "Αν δεν εμφανιστεί ποτέ",
"stop": "Διακοπή της μακροεντολής",
"continue": "Συνέχισε ούτως ή άλλως"
},
"delay": {
"duration": "Περιμένω για"
},
"ifStep": {
"pattern": "Εάν η έξοδος περιέχει",
"patternPlaceholder": "Ενεργό: ενεργό (σε λειτουργία)",
"then": "Στη συνέχεια, κάντε αυτό",
"else": "Διαφορετικά, κάντε αυτό",
"emptyBranch": "Χωρίς σκαλοπάτια"
},
"repeat": {
"count": "Αριθμός φορών",
"times": "φορές"
},
"match": {
"useRegex": "Ταίριασμα με μια κανονική έκφραση",
"useRegexHelp": "Αφήστε το απενεργοποιημένο για να ταιριάζει ακριβώς με το κείμενο όπως είναι γραμμένο.",
"invalidRegex": "Δεν είναι έγκυρη κανονική έκφραση, θα αντιστοιχιστεί ως απλό κείμενο"
},
"seconds": "δευτερόλεπτα",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temas guardados",
"scrollbackBufferLabel": "Búfer de retroceso",
"scrollbackMaxLines": "Número máximo de líneas conservadas en el historial",
"startupSnippetLabel": "Fragmento de inicio",
"sudoPasswordAutoFill": "Autocompletado de contraseña de sudo",
"sudoPasswordAutoFillDesc": "Almacenar una contraseña sudo para que las métricas del host, las solicitudes del terminal y otros puedan ejecutar comandos privilegiados automáticamente.",
"sudoPasswordAutoFillLabel": "Autocompletado de contraseña sudo",
@@ -335,7 +334,8 @@
"history": "Historial",
"localTerminal": "Terminal local",
"sshTools": "Herramientas SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Escriba aquí para transmitir pulsaciones...",
@@ -363,5 +363,112 @@
"off": "Apagado",
"on": "En",
"title": "Valores predeterminados de conexión"
},
"keybindings": {
"copy": "Copiar selección",
"paste": "Pasta",
"sendControlCode": "Enviar señal Ctrl+letra",
"sendText": "Enviar mensaje de texto",
"controlCodeLabel": "Carta",
"textLabel": "Texto para enviar",
"appendEnterLabel": "Presione Enter después de enviar",
"clipboardPermissionNote": "En algunos navegadores, puede solicitar permiso para copiar y pegar el contenido del portapapeles la primera vez que se utilice.",
"textRequired": "Introduce el texto que deseas enviar",
"controlCodeRequired": "Introduzca una sola letra para el código de control."
},
"macros": {
"title": "Macros",
"description": "Ejecuta una serie de comandos guardados en una terminal y reacciona a la respuesta que envíe el servidor.",
"target": "Objetivo",
"noTerminal": "No hay terminal abierta",
"empty": "Aún no hay macros",
"emptyHint": "Crea una macro para automatizar un flujo de terminal, como iniciar sesión, reiniciar un servicio o responder a preguntas.",
"create": "Nueva macro",
"back": "Volver a las macros",
"name": "Nombre",
"namePlaceholder": "Reiniciar el servidor web",
"nameRequired": "Dale un nombre a la macro antes de guardarla.",
"descriptionLabel": "Descripción",
"descriptionPlaceholder": "Qué hace esta macro (opcional)",
"unsaved": "Cambios no guardados",
"save": "Ahorrar",
"saved": "Macro guardada",
"saveFailed": "No se pudo guardar la macro",
"delete": "Borrar",
"deleteTitle": "¿Eliminar esta macro?",
"deleteBody": "\"{{name}}\" se eliminará permanentemente. Esto no se puede deshacer.",
"deleted": "Macro eliminada",
"cancel": "Cancelar",
"run": "Correr",
"stop": "Detener",
"running": "Correr...",
"completed": "Macro terminado",
"failed": "La macro falló",
"loadFailed": "No se pudieron cargar las macros",
"needTerminal": "Primero, abre y conecta una terminal.",
"noSteps": "Esta macro aún no tiene pasos. Añada uno a continuación.",
"addStep": "Agregar paso",
"stepNumber": "Paso {{number}}",
"removeStep": "Eliminar paso",
"confirmRunTitle": "¿Ejecutar esta macro?",
"confirmRunBody": "\"{{name}}\" enviará comandos a {{host}}.",
"confirmRunAction": "Ejecutar macro",
"stepTypes": {
"send": {
"label": "Enviar comando",
"help": "Escribe el texto en la terminal, igual que si lo hubieras escrito tú."
},
"wait": {
"label": "Espere la salida",
"help": "Hace una pausa hasta que el servidor imprime el texto que usted especifique, como si fuera una solicitud de contraseña."
},
"delay": {
"label": "Pausa",
"help": "Espera un tiempo fijo antes de pasar al siguiente paso."
},
"if": {
"label": "Si la salida contiene",
"help": "Comprueba todo lo que el servidor ha impreso hasta el momento y, a continuación, ejecuta una de las dos ramas."
},
"repeat": {
"label": "Repetir",
"help": "Reproduce los pasos que contiene un número determinado de veces."
}
},
"send": {
"text": "Comando o texto",
"textPlaceholder": "systemctl reiniciar nginx",
"pressEnter": "Presione Enter después de enviar",
"pressEnterHelp": "Desactive esta opción para escribir texto sin enviarlo."
},
"wait": {
"pattern": "Espere hasta que la salida contenga",
"patternPlaceholder": "contraseña:",
"timeout": "Rendirse después",
"onTimeout": "Si nunca aparece",
"stop": "Detén la macro",
"continue": "Sigue adelante de todos modos."
},
"delay": {
"duration": "Esperar"
},
"ifStep": {
"pattern": "Si la salida contiene",
"patternPlaceholder": "Activo: activo (corriendo)",
"then": "Entonces haz esto",
"else": "De lo contrario, haz esto",
"emptyBranch": "Sin escalones"
},
"repeat": {
"count": "Número de veces",
"times": "veces"
},
"match": {
"useRegex": "Coincidencia con una expresión regular",
"useRegexHelp": "Omita esta opción para que coincida exactamente con el texto escrito.",
"invalidRegex": "No es una expresión regular válida, se interpretará como texto plano."
},
"seconds": "artículos de segunda clase",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Tallennetut teemat",
"scrollbackBufferLabel": "Vieroituspuskuri",
"scrollbackMaxLines": "Historiassa säilytettävien rivien enimmäismäärä",
"startupSnippetLabel": "Käynnistyskoodinpätkä",
"sudoPasswordAutoFill": "Sudo-salasanan automaattinen täyttö",
"sudoPasswordAutoFillDesc": "Tallenna sudo-salasana, jotta isäntämittarit, päätekehotteet ja muut voivat suorittaa etuoikeutettuja komentoja automaattisesti.",
"sudoPasswordAutoFillLabel": "Sudo-salasanan automaattinen täyttö",
@@ -335,7 +334,8 @@
"history": "Historia",
"localTerminal": "Paikallinen terminaali",
"sshTools": "SSH-työkalut",
"terminal": "Terminaali"
"terminal": "Terminaali",
"macros": "Makrot"
},
"sshTools": {
"broadcastInputPlaceholder": "Kirjoita tähän lähettääksesi näppäinpainalluksia...",
@@ -363,5 +363,112 @@
"off": "Pois",
"on": "Päällä",
"title": "Yhteysoletukset"
},
"keybindings": {
"copy": "Kopioi valinta",
"paste": "Liitä",
"sendControlCode": "Lähetä Ctrl+kirjain-signaali",
"sendText": "Lähetä tekstiviesti",
"controlCodeLabel": "Kirje",
"textLabel": "Lähetettävä tekstiviesti",
"appendEnterLabel": "Paina Enter lähetyksen jälkeen",
"clipboardPermissionNote": "Saattaa pyytää leikepöydän käyttöoikeutta ensimmäisellä käyttökerralla joissakin selaimissa.",
"textRequired": "Kirjoita lähetettävä teksti",
"controlCodeRequired": "Syötä yksi kirjain ohjauskoodille"
},
"macros": {
"title": "Makrot",
"description": "Suorita tallennettu komentosarja terminaalissa ja reagoi palvelimen lähettämiin komentoihin.",
"target": "Kohde",
"noTerminal": "Ei auki olevaa terminaalia",
"empty": "Ei vielä makroja",
"emptyHint": "Luo makro automatisoidaksesi päätetyönkulun, kuten sisäänkirjautumisen, palvelun uudelleenkäynnistyksen tai kehotteisiin vastaamisen.",
"create": "Uusi makro",
"back": "Takaisin makroihin",
"name": "Nimi",
"namePlaceholder": "Käynnistä web-palvelin uudelleen",
"nameRequired": "Anna makrolle nimi ennen tallentamista",
"descriptionLabel": "Kuvaus",
"descriptionPlaceholder": "Mitä tämä makro tekee (valinnainen)",
"unsaved": "Tallentamattomat muutokset",
"save": "Tallentaa",
"saved": "Makro tallennettu",
"saveFailed": "Makroa ei voitu tallentaa",
"delete": "Poistaa",
"deleteTitle": "Poistetaanko tämä makro?",
"deleteBody": "\"{{name}}\" poistetaan pysyvästi. Tätä ei voi perua.",
"deleted": "Makro poistettu",
"cancel": "Peruuttaa",
"run": "Juokse",
"stop": "Stop",
"running": "Juoksemassa...",
"completed": "Makro valmis",
"failed": "Makro epäonnistui",
"loadFailed": "Makrojen lataaminen epäonnistui",
"needTerminal": "Avaa ja kytke ensin päätelaite",
"noSteps": "Tällä makrolla ei ole vielä vaiheita. Lisää yksi alle.",
"addStep": "Lisää vaihe",
"stepNumber": "Vaihe {{number}}",
"removeStep": "Poista vaihe",
"confirmRunTitle": "Suoritetaanko tämä makro?",
"confirmRunBody": "\"{{name}}\" lähettää komentoja {{host}}:een.",
"confirmRunAction": "Suorita makro",
"stepTypes": {
"send": {
"label": "Lähetä komento",
"help": "Kirjoittaa tekstiä terminaaliin aivan kuin olisit kirjoittanut sen."
},
"wait": {
"label": "Odota tulostetta",
"help": "Pysähtyy, kunnes palvelin tulostaa nimeämäsi tekstin, kuten salasanakehotteen."
},
"delay": {
"label": "Tauko",
"help": "Odottaa tietyn ajan ennen seuraavaa vaihetta."
},
"if": {
"label": "Jos tuloste sisältää",
"help": "Tarkistaa kaiken, mitä palvelin on tähän mennessä tulostanut, ja suorittaa sitten toisen kahdesta haarasta."
},
"repeat": {
"label": "Toistaa",
"help": "Suorittaa sen sisällä olevat vaiheet tietyn määrän kertoja."
}
},
"send": {
"text": "Komento tai teksti",
"textPlaceholder": "systemctl käynnistä nginx uudelleen",
"pressEnter": "Paina Enter lähetyksen jälkeen",
"pressEnterHelp": "Poista tämä käytöstä kirjoittaaksesi tekstiä lähettämättä sitä."
},
"wait": {
"pattern": "Odota, kunnes tuloste sisältää",
"patternPlaceholder": "salasana:",
"timeout": "Luovuta jälkeenpäin",
"onTimeout": "Jos se ei koskaan ilmesty",
"stop": "Pysäytä makro",
"continue": "Jatka joka tapauksessa"
},
"delay": {
"duration": "Odota"
},
"ifStep": {
"pattern": "Jos tuloste sisältää",
"patternPlaceholder": "Aktiivinen: aktiivinen (käynnissä)",
"then": "Tee sitten tämä",
"else": "Muuten tee näin",
"emptyBranch": "Ei portaita"
},
"repeat": {
"count": "Kuinka monta kertaa",
"times": "kertaa"
},
"match": {
"useRegex": "Säännöllisen lausekkeen mukainen osuma",
"useRegexHelp": "Jätä tämä pois, jotta teksti vastaa täysin kirjoitettua tekstiä.",
"invalidRegex": "Ei kelvollinen säännöllinen lauseke, se tulkitaan pelkkänä tekstinä"
},
"seconds": "sekuntia",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Thèmes enregistrés",
"scrollbackBufferLabel": "Mémoire de défilement",
"scrollbackMaxLines": "Nombre maximal de lignes conservées dans l'historique",
"startupSnippetLabel": "Extrait de démarrage",
"sudoPasswordAutoFill": "Remplissage automatique du mot de passe sudo",
"sudoPasswordAutoFillDesc": "Enregistrer un mot de passe sudo pour que les métriques de l'hôte, les invites du terminal et autres puissent exécuter automatiquement des commandes privilégiées.",
"sudoPasswordAutoFillLabel": "Remplissage automatique du mot de passe sudo",
@@ -335,7 +334,8 @@
"history": "Historique",
"localTerminal": "Terminal local",
"sshTools": "Outils SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Tapez ici pour diffuser les frappes...",
@@ -363,5 +363,112 @@
"off": "Désactivé",
"on": "Sur",
"title": "Paramètres de connexion par défaut"
},
"keybindings": {
"copy": "Copier la sélection",
"paste": "Coller",
"sendControlCode": "Envoyer le signal Ctrl+lettre",
"sendText": "Envoyer un SMS",
"controlCodeLabel": "Lettre",
"textLabel": "Texte à envoyer",
"appendEnterLabel": "Appuyez sur Entrée après l'envoi",
"clipboardPermissionNote": "Il se peut que certains navigateurs demandent l'autorisation d'accéder au presse-papiers lors de la première utilisation.",
"textRequired": "Saisissez le texte à envoyer",
"controlCodeRequired": "Saisissez une seule lettre pour le code de contrôle"
},
"macros": {
"title": "Macros",
"description": "Exécutez une série de commandes enregistrées dans un terminal et réagissez à la réponse du serveur.",
"target": "Cible",
"noTerminal": "Aucun terminal ouvert",
"empty": "Pas encore de macros",
"emptyHint": "Créez une macro pour automatiser un flux de terminal, comme la connexion, le redémarrage d'un service ou la réponse à des invites.",
"create": "Nouvelle macro",
"back": "Retour aux macros",
"name": "Nom",
"namePlaceholder": "Redémarrer le serveur web",
"nameRequired": "Donnez un nom à la macro avant de l'enregistrer.",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Fonctionnement de cette macro (facultatif)",
"unsaved": "Modifications non enregistrées",
"save": "Sauvegarder",
"saved": "Macro enregistrée",
"saveFailed": "Impossible d'enregistrer la macro",
"delete": "Supprimer",
"deleteTitle": "Supprimer cette macro ?",
"deleteBody": "\"{{name}}\" sera définitivement supprimé. Cette action est irréversible.",
"deleted": "Macro supprimée",
"cancel": "Annuler",
"run": "Courir",
"stop": "Arrêt",
"running": "En cours d'exécution...",
"completed": "Macro terminé",
"failed": "Échec de la macro",
"loadFailed": "Impossible de charger les macros",
"needTerminal": "Ouvrez et connectez d'abord un terminal.",
"noSteps": "Cette macro ne comporte pas encore d'étapes. Ajoutez-en une ci-dessous.",
"addStep": "Ajouter une étape",
"stepNumber": "Étape {{number}}",
"removeStep": "Supprimer l'étape",
"confirmRunTitle": "Exécuter cette macro ?",
"confirmRunBody": "\"{{name}}\" enverra des commandes à {{host}}.",
"confirmRunAction": "Exécuter une macro",
"stepTypes": {
"send": {
"label": "Envoyer la commande",
"help": "Saisit du texte dans le terminal, comme si vous l'aviez saisi manuellement."
},
"wait": {
"label": "Attendez la sortie",
"help": "Il y a une pause jusqu'à ce que le serveur affiche le texte que vous avez indiqué, comme une invite de mot de passe."
},
"delay": {
"label": "Pause",
"help": "Attendre un laps de temps fixe avant de passer à l'étape suivante."
},
"if": {
"label": "Si la sortie contient",
"help": "Vérifie tout ce que le serveur a imprimé jusqu'à présent, puis exécute l'une des deux branches."
},
"repeat": {
"label": "Répéter",
"help": "Exécute les étapes qu'il contient un nombre de fois défini."
}
},
"send": {
"text": "Commande ou texte",
"textPlaceholder": "systemctl redémarrer nginx",
"pressEnter": "Appuyez sur Entrée après l'envoi",
"pressEnterHelp": "Désactivez cette option pour saisir du texte sans le soumettre."
},
"wait": {
"pattern": "Attendez que la sortie contienne",
"patternPlaceholder": "mot de passe:",
"timeout": "Abandonner après",
"onTimeout": "S'il n'apparaît jamais",
"stop": "Arrêtez la macro",
"continue": "Continuez quand même"
},
"delay": {
"duration": "Attendre"
},
"ifStep": {
"pattern": "Si la sortie contient",
"patternPlaceholder": "Actif : actif (en cours d'exécution)",
"then": "Ensuite, faites ceci",
"else": "Sinon, faites ceci.",
"emptyBranch": "Pas d'étapes"
},
"repeat": {
"count": "Nombre de fois",
"times": "fois"
},
"match": {
"useRegex": "Correspondance avec une expression régulière",
"useRegexHelp": "Laissez cette section de côté pour que le texte corresponde exactement à ce qui est écrit.",
"invalidRegex": "Il ne s'agit pas d'une expression régulière valide ; elle sera interprétée comme du texte brut."
},
"seconds": "secondes",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "ערכות נושא שמורות",
"scrollbackBufferLabel": "מאגר גלילה לאחור",
"scrollbackMaxLines": "מספר השורות המרבי שנשמר בהיסטוריה",
"startupSnippetLabel": "קטע הפעלה",
"sudoPasswordAutoFill": "מילוי אוטומטי של סיסמאות Sudo",
"sudoPasswordAutoFillDesc": "אחסן סיסמת sudo כדי שמדדי מארח, הנחיות מסוף ואחרים יוכלו להריץ פקודות בעלות הרשאות באופן אוטומטי.",
"sudoPasswordAutoFillLabel": "מילוי אוטומטי של סיסמת סודו",
@@ -335,7 +334,8 @@
"history": "הִיסטוֹרִיָה",
"localTerminal": "מסוף מקומי",
"sshTools": "כלי SSH",
"terminal": "מָסוֹף"
"terminal": "מָסוֹף",
"macros": "פקודות מאקרו"
},
"sshTools": {
"broadcastInputPlaceholder": "הקלד כאן כדי לשדר הקשות מקלדת...",
@@ -363,5 +363,112 @@
"off": "כבוי",
"on": "עַל",
"title": "ברירות מחדל של חיבור"
},
"keybindings": {
"copy": "העתקת הבחירה",
"paste": "לְהַדבִּיק",
"sendControlCode": "שלח אות Ctrl+אות",
"sendText": "שלח טקסט",
"controlCodeLabel": "מִכְתָב",
"textLabel": "טקסט לשליחה",
"appendEnterLabel": "לחץ על Enter לאחר השליחה",
"clipboardPermissionNote": "ייתכן שתבקש הרשאה ללוח בעת השימוש הראשון בדפדפנים מסוימים.",
"textRequired": "הזן את הטקסט לשליחה",
"controlCodeRequired": "הזן אות אחת עבור קוד הבקרה"
},
"macros": {
"title": "פקודות מאקרו",
"description": "הפעל סדרה שמורה של פקודות בטרמינל, והגב למה שהשרת שולח בחזרה.",
"target": "יַעַד",
"noTerminal": "אין טרמינל פתוח",
"empty": "עדיין אין פקודות מאקרו",
"emptyHint": "צור מאקרו כדי להפוך זרימת מסוף לאוטומטית, כמו כניסה, הפעלה מחדש של שירות או מענה לבקשות.",
"create": "מאקרו חדש",
"back": "חזרה למאקרו",
"name": "שֵׁם",
"namePlaceholder": "הפעל מחדש את שרת האינטרנט",
"nameRequired": "תן שם למאקרו לפני השמירה",
"descriptionLabel": "תֵאוּר",
"descriptionPlaceholder": "מה המאקרו הזה עושה (אופציונלי)",
"unsaved": "שינויים שלא נשמרו",
"save": "לְהַצִיל",
"saved": "מאקרו נשמר",
"saveFailed": "לא ניתן היה לשמור את המאקרו",
"delete": "לִמְחוֹק",
"deleteTitle": "למחוק את המאקרו הזה?",
"deleteBody": "\"{{name}}\" יימחק לצמיתות. לא ניתן לבטל פעולה זו.",
"deleted": "מאקרו נמחק",
"cancel": "לְבַטֵל",
"run": "לָרוּץ",
"stop": "לְהַפְסִיק",
"running": "רִיצָה...",
"completed": "מאקרו הסתיים",
"failed": "מאקרו נכשל",
"loadFailed": "לא ניתן היה לטעון פקודות מאקרו",
"needTerminal": "פתח וחבר תחילה טרמינל",
"noSteps": "למאקרו הזה עדיין אין שלבים. הוסף אחד למטה.",
"addStep": "הוסף שלב",
"stepNumber": "שלב {{number}}",
"removeStep": "הסר שלב",
"confirmRunTitle": "להפעיל את המאקרו הזה?",
"confirmRunBody": "\"{{name}}\" ישלח פקודות אל {{host}}.",
"confirmRunAction": "הפעל מאקרו",
"stepTypes": {
"send": {
"label": "שלח פקודה",
"help": "מקליד טקסט לתוך הטרמינל, אותו הדבר כאילו הקלדת אותו."
},
"wait": {
"label": "המתן לפלט",
"help": "מושהה עד שהשרת ידפיס את הטקסט שציינת, כמו בקשת סיסמה."
},
"delay": {
"label": "הַפסָקָה",
"help": "ממתין פרק זמן קבוע לפני השלב הבא."
},
"if": {
"label": "אם הפלט מכיל",
"help": "בודק את כל מה שהשרת הדפיס עד כה, ולאחר מכן מפעיל אחד משני ענפים."
},
"repeat": {
"label": "לַחֲזוֹר עַל",
"help": "מריץ את השלבים בתוכו מספר קבוע של פעמים."
}
},
"send": {
"text": "פקודה או טקסט",
"textPlaceholder": "הפעל מחדש את nginx של systemctl",
"pressEnter": "לחץ על Enter לאחר השליחה",
"pressEnterHelp": "כבה אפשרות זו כדי להקליד טקסט מבלי לשלוח אותו."
},
"wait": {
"pattern": "המתן עד שהפלט יכיל",
"patternPlaceholder": "סִיסמָה:",
"timeout": "לוותר אחרי",
"onTimeout": "אם זה אף פעם לא מופיע",
"stop": "עצור את המאקרו",
"continue": "המשיכו בכל מקרה"
},
"delay": {
"duration": "חכה ל"
},
"ifStep": {
"pattern": "אם הפלט מכיל",
"patternPlaceholder": "פעיל: פעיל (רץ)",
"then": "אז תעשה את זה",
"else": "אחרת תעשו את זה",
"emptyBranch": "אין מדרגות"
},
"repeat": {
"count": "מספר פעמים",
"times": "פִּי"
},
"match": {
"useRegex": "התאמה עם ביטוי רגולרי",
"useRegexHelp": "השאר את זה כבוי כדי שהטקסט יתאים בדיוק כפי שנכתב.",
"invalidRegex": "ביטוי רגולרי לא תקין, הוא יתאים כטקסט רגיל"
},
"seconds": "שניות",
"secondsShort": "{{count}}ים"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "सहेजे गए विषय",
"scrollbackBufferLabel": "स्क्रॉलबैक बफ़र",
"scrollbackMaxLines": "इतिहास में रखी जाने वाली लाइनों की अधिकतम संख्या",
"startupSnippetLabel": "स्टार्टअप स्निपेट",
"sudoPasswordAutoFill": "sudo पासवर्ड ऑटो-फिल",
"sudoPasswordAutoFillDesc": "sudo पासवर्ड संग्रहीत करें ताकि होस्ट मेट्रिक्स, टर्मिनल संकेत और अन्य स्वचालित रूप से विशेषाधिकार प्राप्त कमांड चला सकें।",
"sudoPasswordAutoFillLabel": "sudo पासवर्ड स्वतः-भरण",
@@ -335,7 +334,8 @@
"history": "इतिहास",
"localTerminal": "स्थानीय टर्मिनल",
"sshTools": "SSH टूल्स",
"terminal": "टर्मिनल"
"terminal": "टर्मिनल",
"macros": "मैक्रो"
},
"sshTools": {
"broadcastInputPlaceholder": "कीस्ट्रोक्स प्रसारित करने के लिए यहाँ टाइप करें...",
@@ -363,5 +363,112 @@
"off": "बंद",
"on": "पर",
"title": "कनेक्शन डिफ़ॉल्ट"
},
"keybindings": {
"copy": "चयन की प्रतिलिपि बनाएँ",
"paste": "पेस्ट करें",
"sendControlCode": "Ctrl+अक्षर सिग्नल भेजें",
"sendText": "संदेश भेजें",
"controlCodeLabel": "पत्र",
"textLabel": "भेजने के लिए संदेश",
"appendEnterLabel": "भेजने के बाद एंटर दबाएँ",
"clipboardPermissionNote": "कुछ ब्राउज़रों में पहली बार उपयोग करने पर क्लिपबोर्ड की अनुमति मांगी जा सकती है।",
"textRequired": "भेजने के लिए टेक्स्ट दर्ज करें",
"controlCodeRequired": "नियंत्रण कोड के लिए एक अक्षर दर्ज करें"
},
"macros": {
"title": "मैक्रो",
"description": "टर्मिनल में सहेजे गए कमांडों की एक श्रृंखला चलाएं, और सर्वर द्वारा वापस भेजे गए कमांडों पर प्रतिक्रिया दें।",
"target": "लक्ष्य",
"noTerminal": "कोई टर्मिनल खुला नहीं है",
"empty": "अभी तक कोई मैक्रो नहीं है",
"emptyHint": "लॉग इन करना, किसी सेवा को रीस्टार्ट करना या प्रॉम्प्ट का जवाब देना जैसे टर्मिनल कार्यों को स्वचालित करने के लिए एक मैक्रो बनाएं।",
"create": "नया मैक्रो",
"back": "मैक्रोज़ पर वापस जाएँ",
"name": "नाम",
"namePlaceholder": "वेब सर्वर को पुनः आरंभ करें",
"nameRequired": "मैक्रो को सेव करने से पहले उसे एक नाम दें।",
"descriptionLabel": "विवरण",
"descriptionPlaceholder": "यह मैक्रो क्या करता है (वैकल्पिक)",
"unsaved": "असहेजित परिवर्तन",
"save": "बचाना",
"saved": "मैक्रो सहेजा गया",
"saveFailed": "मैक्रो को सहेजा नहीं जा सका",
"delete": "मिटाना",
"deleteTitle": "क्या इस मैक्रो को हटा दें?",
"deleteBody": "\"{{name}}\" को स्थायी रूप से हटा दिया जाएगा। इसे पूर्ववत नहीं किया जा सकता है।",
"deleted": "मैक्रो हटा दिया गया",
"cancel": "रद्द करना",
"run": "दौड़ना",
"stop": "रुकना",
"running": "दौड़ना...",
"completed": "मैक्रो समाप्त हो गया",
"failed": "मैक्रो विफल रहा",
"loadFailed": "मैक्रो लोड नहीं हो सके",
"needTerminal": "सबसे पहले एक टर्मिनल खोलें और उससे कनेक्ट करें।",
"noSteps": "इस मैक्रो में अभी तक कोई चरण नहीं है। कृपया नीचे एक चरण जोड़ें।",
"addStep": "चरण जोड़ें",
"stepNumber": "चरण {{number}}",
"removeStep": "चरण हटाएँ",
"confirmRunTitle": "क्या आप इस मैक्रो को चलाना चाहते हैं?",
"confirmRunBody": "\"{{name}}\" {{host}} को कमांड भेजेगा।",
"confirmRunAction": "मैक्रो चलाएँ",
"stepTypes": {
"send": {
"label": "कमांड भेजें",
"help": "यह टर्मिनल में ठीक उसी तरह टेक्स्ट टाइप करता है जैसे आप उसे खुद टाइप करते हैं।"
},
"wait": {
"label": "आउटपुट की प्रतीक्षा करें",
"help": "यह तब तक रुका रहता है जब तक सर्वर आपके द्वारा बताए गए टेक्स्ट को प्रिंट नहीं कर देता, जैसे पासवर्ड प्रॉम्प्ट।"
},
"delay": {
"label": "विराम",
"help": "अगले चरण से पहले एक निश्चित समय तक प्रतीक्षा करता है।"
},
"if": {
"label": "यदि आउटपुट में शामिल है",
"help": "यह सर्वर द्वारा अब तक प्रिंट की गई हर चीज़ की जाँच करता है, फिर दो शाखाओं में से एक को चलाता है।"
},
"repeat": {
"label": "दोहराना",
"help": "इसके अंदर के चरणों को निर्धारित संख्या में बार-बार दोहराता है।"
}
},
"send": {
"text": "आदेश या पाठ",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "भेजने के बाद एंटर दबाएँ",
"pressEnterHelp": "इसे बंद करने पर आपको टेक्स्ट टाइप करने के बाद उसे सबमिट करने की आवश्यकता नहीं होगी।"
},
"wait": {
"pattern": "आउटपुट में यह जानकारी आने तक प्रतीक्षा करें",
"patternPlaceholder": "पासवर्ड:",
"timeout": "हार मान लो",
"onTimeout": "अगर यह कभी प्रकट नहीं होता",
"stop": "मैक्रो को रोकें",
"continue": "फिर भी आगे बढ़ते रहो।"
},
"delay": {
"duration": "के लिए प्रतीक्षा करें"
},
"ifStep": {
"pattern": "यदि आउटपुट में शामिल है",
"patternPlaceholder": "सक्रिय: सक्रिय (चल रहा)",
"then": "फिर ऐसा करें",
"else": "अन्यथा ऐसा करें",
"emptyBranch": "कोई कदम नहीं"
},
"repeat": {
"count": "कितनी बार",
"times": "टाइम्स"
},
"match": {
"useRegex": "नियमित अभिव्यक्ति के साथ मिलान करें",
"useRegexHelp": "इसे हटा दें ताकि यह लिखे गए पाठ से बिल्कुल मेल खाए।",
"invalidRegex": "यह एक मान्य रेगुलर एक्सप्रेशन नहीं है, इसे सादे टेक्स्ट के रूप में ही माना जाएगा।"
},
"seconds": "सेकंड",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Mentett témák",
"scrollbackBufferLabel": "Görgetési puffer",
"scrollbackMaxLines": "A történetben tárolt sorok maximális száma",
"startupSnippetLabel": "Indítási kódrészlet",
"sudoPasswordAutoFill": "Sudo jelszó automatikus kitöltése",
"sudoPasswordAutoFillDesc": "Tároljon egy sudo jelszót, hogy a gazdagép metrikái, a terminálpromptok és mások automatikusan futtathassanak privilegizált parancsokat.",
"sudoPasswordAutoFillLabel": "Sudo jelszó automatikus kitöltése",
@@ -335,7 +334,8 @@
"history": "Történelem",
"localTerminal": "Helyi terminál",
"sshTools": "SSH eszközök",
"terminal": "Terminál"
"terminal": "Terminál",
"macros": "Makrók"
},
"sshTools": {
"broadcastInputPlaceholder": "Írjon ide a billentyűleütések küldéséhez...",
@@ -363,5 +363,112 @@
"off": "Le",
"on": "On",
"title": "Kapcsolat alapértelmezései"
},
"keybindings": {
"copy": "Kijelölés másolása",
"paste": "Paszta",
"sendControlCode": "Ctrl+betűjel küldése",
"sendText": "SMS küldése",
"controlCodeLabel": "Levél",
"textLabel": "Küldendő szöveg",
"appendEnterLabel": "Küldés után nyomja meg az Enter billentyűt",
"clipboardPermissionNote": "Egyes böngészőkben első használatkor vágólap-engedélyt kérhet.",
"textRequired": "Írd be a küldendő szöveget",
"controlCodeRequired": "Írjon be egyetlen betűt az ellenőrző kódhoz"
},
"macros": {
"title": "Makrók",
"description": "Futtass egy mentett parancssorozatot egy terminálban, és reagálj a szerver által küldöttekre.",
"target": "Cél",
"noTerminal": "Nincs nyitva terminál",
"empty": "Még nincsenek makrók",
"emptyHint": "Hozz létre egy makrót egy terminálfolyamat automatizálásához, például bejelentkezéshez, szolgáltatás újraindításához vagy a kérdések megválaszolásához.",
"create": "Új makró",
"back": "Vissza a makrókhoz",
"name": "Név",
"namePlaceholder": "Indítsa újra a webszervert",
"nameRequired": "Mentés előtt adjon nevet a makrónak",
"descriptionLabel": "Leírás",
"descriptionPlaceholder": "Mit csinál ez a makró (opcionális)",
"unsaved": "Nem mentett változtatások",
"save": "Megtakarítás",
"saved": "Makró mentve",
"saveFailed": "Nem sikerült menteni a makrót",
"delete": "Töröl",
"deleteTitle": "Törli ezt a makrót?",
"deleteBody": "A(z) „{{name}}” véglegesen törlésre kerül. Ez a művelet nem vonható vissza.",
"deleted": "Makró törölve",
"cancel": "Mégsem",
"run": "Fut",
"stop": "Stop",
"running": "Futás...",
"completed": "Makró kész",
"failed": "Makró sikertelen",
"loadFailed": "Nem sikerült betölteni a makrókat",
"needTerminal": "Először nyiss meg és csatlakoztass egy terminált",
"noSteps": "Ennek a makrónak még nincsenek lépései. Adjon hozzá egyet alább.",
"addStep": "Lépés hozzáadása",
"stepNumber": "{{number}} lépés",
"removeStep": "Lépés eltávolítása",
"confirmRunTitle": "Futtassa ezt a makrót?",
"confirmRunBody": "A „{{name}}” parancsokat küld a {{host}} címre.",
"confirmRunAction": "Makró futtatása",
"stepTypes": {
"send": {
"label": "Parancs küldése",
"help": "Beírja a szöveget a terminálba, ugyanúgy, mintha beírta volna."
},
"wait": {
"label": "Várjon a kimenetre",
"help": "Szünetet tart, amíg a szerver ki nem írja a megnevezett szöveget, például egy jelszókérést."
},
"delay": {
"label": "Szünet",
"help": "Egy meghatározott ideig vár a következő lépés előtt."
},
"if": {
"label": "Ha a kimenet tartalmaz",
"help": "Ellenőrzi a szerver által eddig kinyomtatott összes adatot, majd lefuttatja a két ág egyikét."
},
"repeat": {
"label": "Ismétlés",
"help": "Meghatározott számú alkalommal futtatja le a benne található lépéseket."
}
},
"send": {
"text": "Parancs vagy szöveg",
"textPlaceholder": "systemctl újraindítja az nginx-et",
"pressEnter": "Küldés után nyomja meg az Enter billentyűt",
"pressEnterHelp": "Kapcsold ki ezt, ha beküldés nélkül szeretnél szöveget beírni."
},
"wait": {
"pattern": "Várjon, amíg a kimenet tartalmazza a",
"patternPlaceholder": "jelszó:",
"timeout": "Add fel utána",
"onTimeout": "Ha soha nem jelenik meg",
"stop": "A makró leállítása",
"continue": "Folytasd mindenképpen"
},
"delay": {
"duration": "Várj"
},
"ifStep": {
"pattern": "Ha a kimenet tartalmaz",
"patternPlaceholder": "Aktív: aktív (fut)",
"then": "Akkor csináld ezt",
"else": "Egyébként tedd ezt",
"emptyBranch": "Nincsenek lépcsők"
},
"repeat": {
"count": "Alkalmak száma",
"times": "alkalommal"
},
"match": {
"useRegex": "Egyezés reguláris kifejezéssel",
"useRegexHelp": "Hagyd ezt ki, hogy a szöveg pontosan úgy illeszkedjen, ahogyan le van írva.",
"invalidRegex": "Nem érvényes reguláris kifejezés, sima szövegként lesz egyezve."
},
"seconds": "másodperc",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Tema Tersimpan",
"scrollbackBufferLabel": "Buffer Gulir Balik",
"scrollbackMaxLines": "Jumlah maksimum baris yang disimpan dalam riwayat",
"startupSnippetLabel": "Cuplikan Startup",
"sudoPasswordAutoFill": "Isi Otomatis Kata Sandi Sudo",
"sudoPasswordAutoFillDesc": "Simpan kata sandi sudo agar metrik host, perintah terminal, dan lainnya dapat menjalankan perintah istimewa secara otomatis.",
"sudoPasswordAutoFillLabel": "Isi Otomatis Kata Sandi Sudo",
@@ -335,7 +334,8 @@
"history": "Sejarah",
"localTerminal": "Terminal Lokal",
"sshTools": "Alat SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makro"
},
"sshTools": {
"broadcastInputPlaceholder": "Ketik di sini untuk menyiarkan ketukan keyboard...",
@@ -363,5 +363,112 @@
"off": "Mati",
"on": "Pada",
"title": "Pengaturan Koneksi Default"
},
"keybindings": {
"copy": "Salin pilihan",
"paste": "Pasta",
"sendControlCode": "Kirim sinyal Ctrl+huruf",
"sendText": "Kirim pesan teks",
"controlCodeLabel": "Surat",
"textLabel": "Teks untuk dikirim",
"appendEnterLabel": "Tekan Enter setelah mengirim",
"clipboardPermissionNote": "Pada beberapa browser, mungkin akan meminta izin akses clipboard saat pertama kali digunakan.",
"textRequired": "Masukkan teks yang ingin dikirim",
"controlCodeRequired": "Masukkan satu huruf untuk kode kontrol."
},
"macros": {
"title": "Makro",
"description": "Jalankan serangkaian perintah yang tersimpan di terminal, dan tanggapi apa yang dikirimkan kembali oleh server.",
"target": "Target",
"noTerminal": "Tidak ada terminal yang terbuka",
"empty": "Belum ada makro",
"emptyHint": "Buat makro untuk mengotomatiskan alur terminal, seperti masuk, memulai ulang layanan, atau menjawab perintah.",
"create": "Makro baru",
"back": "Kembali ke makro",
"name": "Nama",
"namePlaceholder": "Mulai ulang server web",
"nameRequired": "Beri nama makro sebelum menyimpannya.",
"descriptionLabel": "Keterangan",
"descriptionPlaceholder": "Fungsi makro ini (opsional)",
"unsaved": "Perubahan yang belum disimpan",
"save": "Menyimpan",
"saved": "Makro tersimpan",
"saveFailed": "Makro tidak dapat disimpan.",
"delete": "Menghapus",
"deleteTitle": "Hapus makro ini?",
"deleteBody": "\"{{name}}\" akan dihapus secara permanen. Ini tidak dapat dibatalkan.",
"deleted": "Makro dihapus",
"cancel": "Membatalkan",
"run": "Berlari",
"stop": "Berhenti",
"running": "Berlari...",
"completed": "Makro selesai",
"failed": "Makro gagal",
"loadFailed": "Makro tidak dapat dimuat",
"needTerminal": "Buka dan sambungkan terminal terlebih dahulu.",
"noSteps": "Makro ini belum memiliki langkah. Tambahkan langkah di bawah ini.",
"addStep": "Tambahkan langkah",
"stepNumber": "Langkah {{number}}",
"removeStep": "Hapus langkah",
"confirmRunTitle": "Jalankan makro ini?",
"confirmRunBody": "\"{{name}}\" akan mengirimkan perintah ke {{host}}.",
"confirmRunAction": "Jalankan makro",
"stepTypes": {
"send": {
"label": "Kirim perintah",
"help": "Mengetik teks ke terminal, sama seperti jika Anda mengetiknya."
},
"wait": {
"label": "Tunggu hingga muncul output.",
"help": "Akan berhenti sejenak hingga server menampilkan teks yang Anda sebutkan, seperti saat meminta kata sandi."
},
"delay": {
"label": "Berhenti sebentar",
"help": "Menunggu dalam jangka waktu tertentu sebelum melanjutkan ke langkah berikutnya."
},
"if": {
"label": "Jika output berisi",
"help": "Memeriksa semua yang telah dicetak server sejauh ini, lalu menjalankan salah satu dari dua cabang."
},
"repeat": {
"label": "Mengulang",
"help": "Menjalankan langkah-langkah di dalamnya sebanyak jumlah kali yang telah ditentukan."
}
},
"send": {
"text": "Perintah atau teks",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Tekan Enter setelah mengirim",
"pressEnterHelp": "Matikan opsi ini untuk mengetik teks tanpa mengirimkannya."
},
"wait": {
"pattern": "Tunggu hingga output berisi",
"patternPlaceholder": "kata sandi:",
"timeout": "Menyerah setelah",
"onTimeout": "Jika itu tidak pernah muncul",
"stop": "Hentikan makro",
"continue": "Tetaplah melanjutkan"
},
"delay": {
"duration": "Tunggu"
},
"ifStep": {
"pattern": "Jika outputnya berisi",
"patternPlaceholder": "Aktif: aktif (berjalan)",
"then": "Kemudian lakukan ini",
"else": "Jika tidak, lakukan ini",
"emptyBranch": "Tidak ada tangga"
},
"repeat": {
"count": "Jumlah kali",
"times": "waktu"
},
"match": {
"useRegex": "Cocokkan dengan ekspresi reguler",
"useRegexHelp": "Hilangkan bagian ini agar sesuai persis dengan teks yang tertulis.",
"invalidRegex": "Bukan ekspresi reguler yang valid, akan dicocokkan sebagai teks biasa."
},
"seconds": "detik",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temi salvati",
"scrollbackBufferLabel": "Buffer di scorrimento",
"scrollbackMaxLines": "Numero massimo di righe conservate nella cronologia",
"startupSnippetLabel": "Frammento di avvio",
"sudoPasswordAutoFill": "Compilazione automatica password sudo",
"sudoPasswordAutoFillDesc": "Memorizza una password sudo in modo che le metriche dell'host, i prompt del terminale e altro possano eseguire automaticamente comandi privilegiati.",
"sudoPasswordAutoFillLabel": "Compilazione automatica password sudo",
@@ -335,7 +334,8 @@
"history": "Cronologia",
"localTerminal": "Terminale locale",
"sshTools": "Strumenti SSH",
"terminal": "Terminale"
"terminal": "Terminale",
"macros": "Macro"
},
"sshTools": {
"broadcastInputPlaceholder": "Digita qui per trasmettere le pressioni dei tasti...",
@@ -363,5 +363,112 @@
"off": "Spento",
"on": "SU",
"title": "Impostazioni predefinite di connessione"
},
"keybindings": {
"copy": "Copia selezione",
"paste": "Impasto",
"sendControlCode": "Invia il segnale Ctrl+lettera",
"sendText": "Invia testo",
"controlCodeLabel": "Lettera",
"textLabel": "Testo da inviare",
"appendEnterLabel": "Premi Invio dopo aver inviato",
"clipboardPermissionNote": "In alcuni browser potrebbe essere richiesta l'autorizzazione per l'apposizione degli appunti al primo utilizzo.",
"textRequired": "Inserisci il testo da inviare",
"controlCodeRequired": "Inserisci una sola lettera come codice di controllo"
},
"macros": {
"title": "Macro",
"description": "Esegui una serie di comandi salvati in un terminale e reagisci a ciò che il server ti invia in risposta.",
"target": "Bersaglio",
"noTerminal": "Nessun terminale aperto",
"empty": "Ancora nessuna macro",
"emptyHint": "Crea una macro per automatizzare un flusso di lavoro da terminale, come l'accesso, il riavvio di un servizio o la risposta a richieste.",
"create": "Nuova macro",
"back": "Torna alle macro",
"name": "Nome",
"namePlaceholder": "Riavvia il server web",
"nameRequired": "Assegna un nome alla macro prima di salvarla.",
"descriptionLabel": "Descrizione",
"descriptionPlaceholder": "Cosa fa questa macro (facoltativo)",
"unsaved": "Modifiche non salvate",
"save": "Salva",
"saved": "Macro salvata",
"saveFailed": "Impossibile salvare la macro",
"delete": "Eliminare",
"deleteTitle": "Eliminare questa macro?",
"deleteBody": "\"{{name}}\" verrà eliminato definitivamente. Questa operazione non può essere annullata.",
"deleted": "Macro eliminato",
"cancel": "Cancellare",
"run": "Correre",
"stop": "Fermare",
"running": "Corsa...",
"completed": "Macro terminato",
"failed": "Il fallimento di Macro",
"loadFailed": "Impossibile caricare le macro",
"needTerminal": "Aprire e collegare prima un terminale",
"noSteps": "Questa macro non ha ancora passaggi. Aggiungine uno qui sotto.",
"addStep": "Aggiungi passaggio",
"stepNumber": "Passo {{number}}",
"removeStep": "Rimuovere il passaggio",
"confirmRunTitle": "Eseguire questa macro?",
"confirmRunBody": "\"{{name}}\" invierà comandi a {{host}}.",
"confirmRunAction": "Esegui macro",
"stepTypes": {
"send": {
"label": "inviare comando",
"help": "Digita il testo nel terminale, proprio come se lo avessi digitato tu."
},
"wait": {
"label": "Attendi l'output",
"help": "Si mette in pausa finché il server non stampa il testo che hai specificato, ad esempio una richiesta di password."
},
"delay": {
"label": "Pausa",
"help": "Attende un intervallo di tempo fisso prima di procedere al passo successivo."
},
"if": {
"label": "Se l'output contiene",
"help": "Controlla tutto ciò che il server ha stampato finora, quindi esegue uno dei due rami."
},
"repeat": {
"label": "Ripetere",
"help": "Esegue i passaggi al suo interno un numero prestabilito di volte."
}
},
"send": {
"text": "Comando o testo",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Premi Invio dopo aver inviato",
"pressEnterHelp": "Disattiva questa opzione per digitare del testo senza inviarlo."
},
"wait": {
"pattern": "Attendi che l'output contenga",
"patternPlaceholder": "password:",
"timeout": "Rinunciare dopo",
"onTimeout": "Se non appare mai",
"stop": "Interrompi la macro",
"continue": "Continua comunque"
},
"delay": {
"duration": "Attendi"
},
"ifStep": {
"pattern": "Se l'output contiene",
"patternPlaceholder": "Attivo: attivo (in corsa)",
"then": "Poi fai questo",
"else": "Altrimenti fai così",
"emptyBranch": "Nessun gradino"
},
"repeat": {
"count": "Numero di volte",
"times": "volte"
},
"match": {
"useRegex": "Corrispondenza con un'espressione regolare",
"useRegexHelp": "Lasciare vuoto questo elemento per far sì che il testo corrisponda esattamente a come è stato scritto.",
"invalidRegex": "Non è un'espressione regolare valida, verrà interpretata come testo semplice."
},
"seconds": "secondi",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "保存済みテーマ",
"scrollbackBufferLabel": "スクロールバッファ",
"scrollbackMaxLines": "履歴に保持する最大行数",
"startupSnippetLabel": "起動時スニペット",
"sudoPasswordAutoFill": "Sudo パスワードの自動入力",
"sudoPasswordAutoFillDesc": "sudo パスワードを保存して、ホストメトリクス、ターミナルプロンプトなどが自動的に特権コマンドを実行できるようにします",
"sudoPasswordAutoFillLabel": "sudo パスワードの自動入力",
@@ -335,7 +334,8 @@
"history": "履歴",
"localTerminal": "ローカル端末",
"sshTools": "SSHツール",
"terminal": "ターミナル"
"terminal": "ターミナル",
"macros": "マクロ"
},
"sshTools": {
"broadcastInputPlaceholder": "ここに入力してキーをブロードキャスト...",
@@ -363,5 +363,112 @@
"off": "オフ",
"on": "の上",
"title": "接続のデフォルト設定"
},
"keybindings": {
"copy": "選択範囲をコピー",
"paste": "ペースト",
"sendControlCode": "Ctrlキーと文字キーを同時に押して信号を送る",
"sendText": "テキストを送信",
"controlCodeLabel": "手紙",
"textLabel": "送信するテキスト",
"appendEnterLabel": "送信後にEnterキーを押してください。",
"clipboardPermissionNote": "一部のブラウザでは、初回使用時にクリップボードのアクセス許可を求められる場合があります。",
"textRequired": "送信するテキストを入力してください",
"controlCodeRequired": "制御コードとして1文字を入力してください"
},
"macros": {
"title": "マクロ",
"description": "ターミナルで保存済みのコマンド系列を実行し、サーバーからの応答に応じて処理を行います。",
"target": "ターゲット",
"noTerminal": "端末が開いていません",
"empty": "マクロはまだありません",
"emptyHint": "ログイン、サービスの再起動、プロンプトへの応答など、ターミナル操作の流れを自動化するマクロを作成します。",
"create": "新しいマクロ",
"back": "マクロに戻る",
"name": "名前",
"namePlaceholder": "ウェブサーバーを再起動します",
"nameRequired": "保存する前にマクロに名前を付けてください。",
"descriptionLabel": "説明",
"descriptionPlaceholder": "このマクロの機能(オプション)",
"unsaved": "保存されていない変更",
"save": "保存",
"saved": "マクロが保存されました",
"saveFailed": "マクロを保存できませんでした",
"delete": "消去",
"deleteTitle": "このマクロを削除しますか?",
"deleteBody": "\"{{name}}\" は完全に削除されます。元に戻すことはできません。",
"deleted": "マクロが削除されました",
"cancel": "キャンセル",
"run": "走る",
"stop": "停止",
"running": "実行中...",
"completed": "マクロ撮影終了",
"failed": "マクロが失敗しました",
"loadFailed": "マクロを読み込めませんでした",
"needTerminal": "まずターミナルを開いて接続します。",
"noSteps": "このマクロにはまだ手順がありません。以下に手順を追加してください。",
"addStep": "ステップを追加する",
"stepNumber": "ステップ {{number}}",
"removeStep": "削除手順",
"confirmRunTitle": "このマクロを実行しますか?",
"confirmRunBody": "\"{{name}}\" は {{host}} にコマンドを送信します。",
"confirmRunAction": "マクロを実行する",
"stepTypes": {
"send": {
"label": "コマンドを送信",
"help": "端末にテキストを入力します。まるで手入力したかのように表示されます。"
},
"wait": {
"label": "出力を待つ",
"help": "サーバーが指定したテキスト(パスワード入力画面など)を表示するまで一時停止します。"
},
"delay": {
"label": "一時停止",
"help": "次のステップに進む前に、一定時間待機します。"
},
"if": {
"label": "出力に以下が含まれる場合",
"help": "サーバーがこれまでに出力したすべての内容を確認し、その後、2つの分岐のうちいずれか1つを実行します。"
},
"repeat": {
"label": "繰り返す",
"help": "指定された回数だけ、その中の手順を実行します。"
}
},
"send": {
"text": "コマンドまたはテキスト",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "送信後にEnterキーを押してください。",
"pressEnterHelp": "テキストを送信せずに入力する場合は、これをオフにしてください。"
},
"wait": {
"pattern": "出力に以下の内容が含まれるまで待機してください",
"patternPlaceholder": "パスワード:",
"timeout": "諦めた後",
"onTimeout": "それが決して現れない場合",
"stop": "マクロを停止する",
"continue": "とにかく続けて"
},
"delay": {
"duration": "待って"
},
"ifStep": {
"pattern": "出力に以下が含まれる場合",
"patternPlaceholder": "アクティブ:アクティブ(実行中)",
"then": "次にこれを実行してください",
"else": "そうでなければ、これを実行してください。",
"emptyBranch": "階段なし"
},
"repeat": {
"count": "回数",
"times": "タイムズ"
},
"match": {
"useRegex": "正規表現で一致させる",
"useRegexHelp": "原文と完全に一致させるには、この部分を削除してください。",
"invalidRegex": "有効な正規表現ではないため、プレーンテキストとしてマッチします。"
},
"seconds": "秒",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "저장된 테마",
"scrollbackBufferLabel": "스크롤백 버퍼",
"scrollbackMaxLines": "기록에 유지할 최대 라인 수",
"startupSnippetLabel": "시작 스니펫",
"sudoPasswordAutoFill": "Sudo 비밀번호 자동 완성",
"sudoPasswordAutoFillDesc": "sudo 비밀번호를 저장하여 호스트 메트릭, 터미널 프롬프트 등이 자동으로 권한 있는 명령을 실행할 수 있도록 합니다",
"sudoPasswordAutoFillLabel": "Sudo 비밀번호 자동 입력",
@@ -335,7 +334,8 @@
"history": "기록",
"localTerminal": "로컬 터미널",
"sshTools": "SSH 도구",
"terminal": "터미널"
"terminal": "터미널",
"macros": "매크로"
},
"sshTools": {
"broadcastInputPlaceholder": "키 입력을 브로드캐스트하려면 여기에 입력...",
@@ -363,5 +363,112 @@
"off": "끄다",
"on": "~에",
"title": "연결 기본 설정"
},
"keybindings": {
"copy": "선택 복사",
"paste": "반죽",
"sendControlCode": "Ctrl+문자 신호를 보내세요",
"sendText": "문자 보내기",
"controlCodeLabel": "편지",
"textLabel": "보낼 텍스트",
"appendEnterLabel": "전송 후 Enter 키를 누르세요.",
"clipboardPermissionNote": "일부 브라우저에서는 처음 사용 시 클립보드 권한을 요청할 수 있습니다.",
"textRequired": "보낼 텍스트를 입력하세요",
"controlCodeRequired": "제어 코드에 한 글자를 입력하세요."
},
"macros": {
"title": "매크로",
"description": "터미널에서 저장된 일련의 명령어를 실행하고, 서버가 보내는 응답에 반응합니다.",
"target": "목표",
"noTerminal": "터미널이 열려 있지 않습니다",
"empty": "아직 매크로가 없습니다.",
"emptyHint": "터미널 로그인, 서비스 재시작 또는 프롬프트 응답과 같은 터미널 작업 흐름을 자동화하는 매크로를 만드세요.",
"create": "새로운 매크로",
"back": "매크로로 돌아가기",
"name": "이름",
"namePlaceholder": "웹 서버를 재시작하세요",
"nameRequired": "매크로를 저장하기 전에 이름을 지정하세요.",
"descriptionLabel": "설명",
"descriptionPlaceholder": "이 매크로의 기능 (선택 사항)",
"unsaved": "저장되지 않은 변경 사항",
"save": "구하다",
"saved": "매크로 저장됨",
"saveFailed": "매크로를 저장할 수 없습니다.",
"delete": "삭제",
"deleteTitle": "이 매크로를 삭제하시겠습니까?",
"deleteBody": "\"{{name}}\"는 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다.",
"deleted": "매크로 삭제됨",
"cancel": "취소",
"run": "달리다",
"stop": "멈추다",
"running": "달리기...",
"completed": "매크로 완료",
"failed": "매크로 실패",
"loadFailed": "매크로를 불러올 수 없습니다.",
"needTerminal": "먼저 터미널을 열고 연결하세요.",
"noSteps": "이 매크로는 아직 단계가 없습니다. 아래에 단계를 추가하세요.",
"addStep": "단계 추가",
"stepNumber": "단계 {{number}}",
"removeStep": "제거 단계",
"confirmRunTitle": "이 매크로를 실행하시겠습니까?",
"confirmRunBody": "\"{{name}}\"는 {{host}}로 명령을 보냅니다.",
"confirmRunAction": "매크로 실행",
"stepTypes": {
"send": {
"label": "명령을 보내세요",
"help": "터미널에 텍스트를 입력합니다. 마치 직접 타이핑하는 것과 같습니다."
},
"wait": {
"label": "출력이 나올 때까지 기다리세요",
"help": "서버가 사용자가 입력한 텍스트(예: 비밀번호 입력창)를 출력할 때까지 일시 중지됩니다."
},
"delay": {
"label": "정지시키다",
"help": "다음 단계로 넘어가기 전에 일정 시간 동안 기다립니다."
},
"if": {
"label": "출력에 다음 내용이 포함되어 있는 경우",
"help": "서버에서 지금까지 출력한 모든 내용을 확인한 후 두 가지 분기 중 하나를 실행합니다."
},
"repeat": {
"label": "반복하다",
"help": "그 안에 있는 단계들을 정해진 횟수만큼 실행합니다."
}
},
"send": {
"text": "명령 또는 텍스트",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "전송 후 Enter 키를 누르세요.",
"pressEnterHelp": "텍스트를 입력하되 제출하지 않으려면 이 기능을 끄세요."
},
"wait": {
"pattern": "출력 내용이 포함될 때까지 기다리세요",
"patternPlaceholder": "비밀번호:",
"timeout": "포기한 후에",
"onTimeout": "만약 전혀 나타나지 않는다면",
"stop": "매크로를 중지하세요",
"continue": "어쨌든 계속 가세요"
},
"delay": {
"duration": "기다리세요"
},
"ifStep": {
"pattern": "출력에 다음 내용이 포함되어 있는 경우",
"patternPlaceholder": "활성: 활성(실행 중)",
"then": "그럼 이렇게 하세요",
"else": "그렇지 않으면 이렇게 하세요.",
"emptyBranch": "단계 없음"
},
"repeat": {
"count": "횟수",
"times": "타임스"
},
"match": {
"useRegex": "정규 표현식과 일치",
"useRegexHelp": "원문과 정확히 일치하려면 이 부분을 삭제하세요.",
"invalidRegex": "유효한 정규 표현식이 아니므로 일반 텍스트로 처리됩니다."
},
"seconds": "초",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Opgeslagen thema's",
"scrollbackBufferLabel": "Terugscrollbuffer",
"scrollbackMaxLines": "Maximaal aantal regels dat in de geschiedenis wordt bewaard",
"startupSnippetLabel": "Opstartfragment",
"sudoPasswordAutoFill": "Sudo-wachtwoord automatisch invullen",
"sudoPasswordAutoFillDesc": "Sla een sudo-wachtwoord op, zodat hoststatistieken, terminalprompts en andere programma's automatisch geprivilegieerde opdrachten kunnen uitvoeren.",
"sudoPasswordAutoFillLabel": "Sudo-wachtwoord automatisch invullen",
@@ -335,7 +334,8 @@
"history": "Geschiedenis",
"localTerminal": "Lokale terminal",
"sshTools": "SSH-tools",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macro's"
},
"sshTools": {
"broadcastInputPlaceholder": "Typ hier om toetsaanslagen te verzenden...",
@@ -363,5 +363,112 @@
"off": "Uit",
"on": "Op",
"title": "Verbindingsstandaarden"
},
"keybindings": {
"copy": "Selectie kopiëren",
"paste": "Plakken",
"sendControlCode": "Verzend het Ctrl+letter-signaal",
"sendText": "Sms verzenden",
"controlCodeLabel": "Brief",
"textLabel": "Tekst om te verzenden",
"appendEnterLabel": "Druk op Enter na het verzenden.",
"clipboardPermissionNote": "In sommige browsers kan bij het eerste gebruik om toestemming voor het gebruik van het klembord worden gevraagd.",
"textRequired": "Voer de tekst in die u wilt verzenden",
"controlCodeRequired": "Voer één letter in als besturingscode."
},
"macros": {
"title": "Macro's",
"description": "Voer een opgeslagen reeks commando's uit in een terminal en reageer op wat de server terugstuurt.",
"target": "Doel",
"noTerminal": "Geen terminal geopend",
"empty": "Nog geen macro's",
"emptyHint": "Maak een macro om een terminalproces te automatiseren, zoals inloggen, een service herstarten of prompts beantwoorden.",
"create": "Nieuwe macro",
"back": "Terug naar macro's",
"name": "Naam",
"namePlaceholder": "Webserver opnieuw opstarten",
"nameRequired": "Geef de macro een naam voordat je hem opslaat.",
"descriptionLabel": "Beschrijving",
"descriptionPlaceholder": "Wat deze macro doet (optioneel)",
"unsaved": "Niet-opgeslagen wijzigingen",
"save": "Redden",
"saved": "Macro opgeslagen",
"saveFailed": "Het lukte niet om de macro op te slaan.",
"delete": "Verwijderen",
"deleteTitle": "Deze macro verwijderen?",
"deleteBody": "\"{{name}}\" wordt permanent verwijderd. Dit kan niet ongedaan gemaakt worden.",
"deleted": "Macro verwijderd",
"cancel": "Annuleren",
"run": "Loop",
"stop": "Stop",
"running": "Rennen...",
"completed": "Macro voltooid",
"failed": "Macro mislukt",
"loadFailed": "Macro's konden niet worden geladen.",
"needTerminal": "Open en verbind eerst een terminal.",
"noSteps": "Deze macro bevat nog geen stappen. Voeg er hieronder een toe.",
"addStep": "Voeg stap toe",
"stepNumber": "Stap {{number}}",
"removeStep": "Verwijder stap",
"confirmRunTitle": "Voer deze macro uit?",
"confirmRunBody": "\"{{name}}\" zal commando's naar {{host}} sturen.",
"confirmRunAction": "Macro uitvoeren",
"stepTypes": {
"send": {
"label": "Verzend commando",
"help": "Typt tekst in de terminal, net alsof je die zelf hebt ingetypt."
},
"wait": {
"label": "Wacht op uitvoer",
"help": "Het programma pauzeert totdat de server de door u opgegeven tekst afdrukt, zoals bij een wachtwoordprompt."
},
"delay": {
"label": "Pauze",
"help": "Wacht een vaste tijdsduur voordat de volgende stap wordt uitgevoerd."
},
"if": {
"label": "Als de uitvoer het volgende bevat:",
"help": "Controleert alles wat de server tot nu toe heeft afgedrukt en voert vervolgens een van de twee vertakkingen uit."
},
"repeat": {
"label": "Herhalen",
"help": "Voert de stappen binnenin een vast aantal keren uit."
}
},
"send": {
"text": "Opdracht of tekst",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Druk op Enter na het verzenden.",
"pressEnterHelp": "Schakel deze optie uit om tekst te typen zonder deze te verzenden."
},
"wait": {
"pattern": "Wacht tot de uitvoer het volgende bevat:",
"patternPlaceholder": "wachtwoord:",
"timeout": "Geef het op na",
"onTimeout": "Als het nooit verschijnt",
"stop": "Stop de macro",
"continue": "Ga toch door."
},
"delay": {
"duration": "Wachten op"
},
"ifStep": {
"pattern": "Als de uitvoer het volgende bevat:",
"patternPlaceholder": "Actief: actief (lopend)",
"then": "Doe dan dit",
"else": "Doe anders dit",
"emptyBranch": "Geen treden"
},
"repeat": {
"count": "Aantal keren",
"times": "tijden"
},
"match": {
"useRegex": "Overeenkomst met een reguliere expressie",
"useRegexHelp": "Laat dit weg zodat de tekst exact overeenkomt met de geschreven tekst.",
"invalidRegex": "Dit is geen geldige reguliere expressie; het wordt als platte tekst herkend."
},
"seconds": "seconden",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Lagrede temaer",
"scrollbackBufferLabel": "Tilbakespolingsbuffer",
"scrollbackMaxLines": "Maksimalt antall linjer lagret i historikken",
"startupSnippetLabel": "Oppstartsutdrag",
"sudoPasswordAutoFill": "Sudo-passord automatisk utfylling",
"sudoPasswordAutoFillDesc": "Lagre et sudo-passord slik at vertsmålinger, terminalledetekster og andre kan kjøre privilegerte kommandoer automatisk.",
"sudoPasswordAutoFillLabel": "Sudo-passord automatisk utfylling",
@@ -335,7 +334,8 @@
"history": "Historie",
"localTerminal": "Lokal terminal",
"sshTools": "SSH-verktøy",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makroer"
},
"sshTools": {
"broadcastInputPlaceholder": "Skriv her for å kringkaste tastetrykk...",
@@ -363,5 +363,112 @@
"off": "Av",
"on": "På",
"title": "Standardinnstillinger for tilkobling"
},
"keybindings": {
"copy": "Kopier valg",
"paste": "Lim inn",
"sendControlCode": "Send Ctrl+bokstavsignal",
"sendText": "Send tekstmelding",
"controlCodeLabel": "Brev",
"textLabel": "Tekst som skal sendes",
"appendEnterLabel": "Trykk Enter etter sending",
"clipboardPermissionNote": "Kan be om tillatelse til utklippstavlen ved første gangs bruk i noen nettlesere.",
"textRequired": "Skriv inn teksten som skal sendes",
"controlCodeRequired": "Skriv inn én bokstav for kontrollkoden"
},
"macros": {
"title": "Makroer",
"description": "Kjør en lagret serie med kommandoer i en terminal, og reager på det serveren sender tilbake.",
"target": "Mål",
"noTerminal": "Ingen terminal åpen",
"empty": "Ingen makroer ennå",
"emptyHint": "Opprett en makro for å automatisere en terminalflyt, som å logge inn, starte en tjeneste på nytt eller svare på forespørsler.",
"create": "Ny makro",
"back": "Tilbake til makroer",
"name": "Navn",
"namePlaceholder": "Start nettserveren på nytt",
"nameRequired": "Gi makroen et navn før du lagrer",
"descriptionLabel": "Beskrivelse",
"descriptionPlaceholder": "Hva denne makroen gjør (valgfritt)",
"unsaved": "Ulagrede endringer",
"save": "Spare",
"saved": "Makro lagret",
"saveFailed": "Kunne ikke lagre makroen",
"delete": "Slett",
"deleteTitle": "Slette denne makroen?",
"deleteBody": "«{{name}}» vil bli slettet permanent. Dette kan ikke angres.",
"deleted": "Makro slettet",
"cancel": "Kansellere",
"run": "Løp",
"stop": "Stoppe",
"running": "Løping...",
"completed": "Makro ferdig",
"failed": "Makroen mislyktes",
"loadFailed": "Kunne ikke laste inn makroer",
"needTerminal": "Åpne og koble til en terminal først",
"noSteps": "Denne makroen har ingen trinn ennå. Legg til et nedenfor.",
"addStep": "Legg til trinn",
"stepNumber": "Trinn {{number}}",
"removeStep": "Fjern trinn",
"confirmRunTitle": "Kjøre denne makroen?",
"confirmRunBody": "\"{{name}}\" vil sende kommandoer til {{host}}.",
"confirmRunAction": "Kjør makro",
"stepTypes": {
"send": {
"label": "Send kommando",
"help": "Skriver tekst inn i terminalen, på samme måte som om du hadde skrevet den."
},
"wait": {
"label": "Vent på utdata",
"help": "Pauserer til serveren skriver ut teksten du oppgir, som en passordforespørsel."
},
"delay": {
"label": "Pause",
"help": "Venter en fast tid før neste trinn."
},
"if": {
"label": "Hvis utdataene inneholder",
"help": "Sjekker alt serveren har skrevet ut så langt, og kjører deretter en av to grener."
},
"repeat": {
"label": "Gjenta",
"help": "Kjører trinnene inni den et angitt antall ganger."
}
},
"send": {
"text": "Kommando eller tekst",
"textPlaceholder": "systemctl omstart nginx",
"pressEnter": "Trykk Enter etter sending",
"pressEnterHelp": "Slå av dette for å skrive tekst uten å sende den inn."
},
"wait": {
"pattern": "Vent til utdataene inneholder",
"patternPlaceholder": "passord:",
"timeout": "Gi opp etterpå",
"onTimeout": "Hvis den aldri dukker opp",
"stop": "Stopp makroen",
"continue": "Fortsett uansett"
},
"delay": {
"duration": "Vent på"
},
"ifStep": {
"pattern": "Hvis utdataene inneholder",
"patternPlaceholder": "Aktiv: aktiv (løper)",
"then": "Gjør så dette",
"else": "Ellers gjør dette",
"emptyBranch": "Ingen trinn"
},
"repeat": {
"count": "Antall ganger",
"times": "ganger"
},
"match": {
"useRegex": "Samsvar med et regulært uttrykk",
"useRegexHelp": "La dette være av for å få teksten til å se nøyaktig slik den er skrevet.",
"invalidRegex": "Ikke et gyldig regulært uttrykk, det vil bli matchet som ren tekst"
},
"seconds": "sekunder",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Zapisane motywy",
"scrollbackBufferLabel": "Bufor przewijania",
"scrollbackMaxLines": "Maksymalna liczba wierszy przechowywanych w historii",
"startupSnippetLabel": "Snippet startowy",
"sudoPasswordAutoFill": "Automatyczne uzupełnianie hasła sudo",
"sudoPasswordAutoFillDesc": "Przechowuj hasło sudo, aby metryki hosta, monity terminala i inne narzędzia mogły automatycznie uruchamiać polecenia uprzywilejowane.",
"sudoPasswordAutoFillLabel": "Automatyczne uzupełnianie hasła sudo",
@@ -335,7 +334,8 @@
"history": "Historia",
"localTerminal": "Terminal lokalny",
"sshTools": "Narzędzia SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makra"
},
"sshTools": {
"broadcastInputPlaceholder": "Wpisz tutaj, aby rozesłać naciśnięcia klawiszy...",
@@ -363,5 +363,112 @@
"off": "Wył.",
"on": "Wł.",
"title": "Domyślne ustawienia połączenia"
},
"keybindings": {
"copy": "Kopiuj zaznaczenie",
"paste": "Wklej",
"sendControlCode": "Wyślij sygnał Ctrl+litera",
"sendText": "Wyślij tekst",
"controlCodeLabel": "Litera",
"textLabel": "Tekst do wysłania",
"appendEnterLabel": "Naciśnij Enter po wysłaniu",
"clipboardPermissionNote": "Przy pierwszym użyciu w niektórych przeglądarkach może zostać wyświetlony monit o zezwolenie na dostęp do schowka.",
"textRequired": "Wprowadź tekst do wysłania",
"controlCodeRequired": "Wprowadź pojedynczą literę kodu kontrolnego"
},
"macros": {
"title": "Makra",
"description": "Uruchom zapisaną serię poleceń w terminalu i zareaguj na to, co odeśle serwer.",
"target": "Cel",
"noTerminal": "Brak otwartego terminala",
"empty": "Brak makr",
"emptyHint": "Utwórz makro, aby zautomatyzować pracę w terminalu: logowanie, restart usługi czy odpowiadanie na monity.",
"create": "Nowe makro",
"back": "Powrót do makr",
"name": "Nazwa",
"namePlaceholder": "Uruchom ponownie serwer WWW",
"nameRequired": "Przed zapisaniem nadaj makrze nazwę",
"descriptionLabel": "Opis",
"descriptionPlaceholder": "Co robi to makro (opcjonalnie)",
"unsaved": "Niezapisane zmiany",
"save": "Zapisz",
"saved": "Zapisano makro",
"saveFailed": "Nie udało się zapisać makra",
"delete": "Usuń",
"deleteTitle": "Usunąć to makro?",
"deleteBody": "„{{name}}” zostanie trwale usunięte. Nie można tego cofnąć.",
"deleted": "Usunięto makro",
"cancel": "Anuluj",
"run": "Uruchom",
"stop": "Zatrzymaj",
"running": "Działa...",
"completed": "Makro zakończone",
"failed": "Makro nie powiodło się",
"loadFailed": "Nie można załadować makr",
"needTerminal": "Najpierw otwórz i podłącz terminal",
"noSteps": "To makro nie ma jeszcze żadnych kroków. Dodaj krok poniżej.",
"addStep": "Dodaj krok",
"stepNumber": "Krok {{number}}",
"removeStep": "Usuń krok",
"confirmRunTitle": "Uruchomić to makro?",
"confirmRunBody": "„{{name}}” wyśle polecenia do {{host}}.",
"confirmRunAction": "Uruchom makro",
"stepTypes": {
"send": {
"label": "Wyślij polecenie",
"help": "Wpisuje tekst do terminala, tak samo, jakby wpisano go ręcznie."
},
"wait": {
"label": "Czekaj na wyjście",
"help": "Wstrzymuje wykonanie, aż serwer wypisze podany tekst, np. monit o hasło."
},
"delay": {
"label": "Pauza",
"help": "Czeka przez ustalony czas przed kolejnym krokiem."
},
"if": {
"label": "Jeśli wyjście zawiera",
"help": "Sprawdza wszystko, co serwer wypisał do tej pory, a potem uruchamia jedną z dwóch gałęzi."
},
"repeat": {
"label": "Powtórz",
"help": "Uruchamia zawarte w nim kroki ustaloną liczbę razy."
}
},
"send": {
"text": "Polecenie lub tekst",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Naciśnij Enter po wysłaniu",
"pressEnterHelp": "Wyłącz tę opcję, aby wpisać tekst bez przesyłania go."
},
"wait": {
"pattern": "Czekaj, aż wyjście będzie zawierać",
"patternPlaceholder": "password:",
"timeout": "Przerwij po",
"onTimeout": "Jeśli nigdy się nie pojawi",
"stop": "Zatrzymaj makro",
"continue": "Kontynuuj mimo wszystko"
},
"delay": {
"duration": "Czekaj"
},
"ifStep": {
"pattern": "Jeśli wyjście zawiera",
"patternPlaceholder": "Active: active (running)",
"then": "Następnie zrób to",
"else": "W przeciwnym razie zrób to",
"emptyBranch": "Brak kroków"
},
"repeat": {
"count": "Liczba razy",
"times": "razy"
},
"match": {
"useRegex": "Dopasuj do wyrażenia regularnego",
"useRegexHelp": "Zostaw wyłączone, aby dopasować tekst dokładnie tak, jak został wpisany.",
"invalidRegex": "Nie jest to prawidłowe wyrażenie regularne, zostanie dopasowane jako zwykły tekst"
},
"seconds": "sekundy",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temas salvos",
"scrollbackBufferLabel": "Buffer de rolagem",
"scrollbackMaxLines": "Número máximo de linhas mantidas no histórico",
"startupSnippetLabel": "Trecho de startup",
"sudoPasswordAutoFill": "Preenchimento automático de senhas Sudo",
"sudoPasswordAutoFillDesc": "Armazene uma senha de sudo para que as métricas do host, os prompts do terminal e outros possam executar comandos privilegiados automaticamente.",
"sudoPasswordAutoFillLabel": "Preenchimento automático de senha Sudo",
@@ -335,7 +334,8 @@
"history": "História",
"localTerminal": "Terminal local",
"sshTools": "Ferramentas SSH",
"terminal": "terminal"
"terminal": "terminal",
"macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Digite aqui para transmitir as teclas pressionadas...",
@@ -363,5 +363,112 @@
"off": "Desligado",
"on": "Sobre",
"title": "Configurações de conexão padrão"
},
"keybindings": {
"copy": "Copiar seleção",
"paste": "Colar",
"sendControlCode": "Enviar sinal Ctrl+letra",
"sendText": "Enviar mensagem de texto",
"controlCodeLabel": "Carta",
"textLabel": "Texto a enviar",
"appendEnterLabel": "Pressione Enter após enviar",
"clipboardPermissionNote": "Pode solicitar permissão para acessar a área de transferência no primeiro uso em alguns navegadores.",
"textRequired": "Digite o texto que deseja enviar.",
"controlCodeRequired": "Digite uma única letra para o código de controle."
},
"macros": {
"title": "Macros",
"description": "Execute uma série de comandos salvos em um terminal e reaja ao que o servidor envia de volta.",
"target": "Alvo",
"noTerminal": "Nenhum terminal aberto",
"empty": "Ainda não há macros.",
"emptyHint": "Crie uma macro para automatizar um fluxo de trabalho no terminal, como fazer login, reiniciar um serviço ou responder a solicitações.",
"create": "Nova macro",
"back": "Voltar às macros",
"name": "Nome",
"namePlaceholder": "Reinicie o servidor web.",
"nameRequired": "Dê um nome à macro antes de salvar.",
"descriptionLabel": "Descrição",
"descriptionPlaceholder": "O que esta macro faz (opcional)",
"unsaved": "Alterações não salvas",
"save": "Salvar",
"saved": "Macro salvo",
"saveFailed": "Não foi possível salvar a macro.",
"delete": "Excluir",
"deleteTitle": "Excluir esta macro?",
"deleteBody": "\"{{name}}\" será excluído permanentemente. Esta ação não pode ser desfeita.",
"deleted": "Macro excluída",
"cancel": "Cancelar",
"run": "Correr",
"stop": "Parar",
"running": "Correndo...",
"completed": "Macro finalizado",
"failed": "A macro falhou",
"loadFailed": "Não foi possível carregar as macros",
"needTerminal": "Primeiro, abra e conecte um terminal.",
"noSteps": "Esta macro ainda não possui etapas. Adicione uma abaixo.",
"addStep": "Adicionar etapa",
"stepNumber": "Passo {{number}}",
"removeStep": "Remover etapa",
"confirmRunTitle": "Executar esta macro?",
"confirmRunBody": "\"{{name}}\" enviará comandos para {{host}}.",
"confirmRunAction": "Executar macro",
"stepTypes": {
"send": {
"label": "Enviar comando",
"help": "Digita o texto no terminal, da mesma forma que se você o digitasse."
},
"wait": {
"label": "Aguarde a saída",
"help": "Aguarda até que o servidor imprima o texto que você digitou, como uma solicitação de senha."
},
"delay": {
"label": "Pausa",
"help": "Aguarda um período de tempo fixo antes da próxima etapa."
},
"if": {
"label": "Se a saída contiver",
"help": "Verifica tudo o que o servidor imprimiu até o momento e, em seguida, executa uma das duas ramificações."
},
"repeat": {
"label": "Repita",
"help": "Executa os passos internos um número definido de vezes."
}
},
"send": {
"text": "Comando ou texto",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Pressione Enter após enviar",
"pressEnterHelp": "Desative esta opção para digitar texto sem enviá-lo."
},
"wait": {
"pattern": "Aguarde até que a saída contenha",
"patternPlaceholder": "senha:",
"timeout": "Desistir depois",
"onTimeout": "Se nunca aparecer",
"stop": "Pare a macro",
"continue": "Continue mesmo assim"
},
"delay": {
"duration": "Aguarde"
},
"ifStep": {
"pattern": "Se a saída contiver",
"patternPlaceholder": "Ativo: ativo (em execução)",
"then": "Então faça isso",
"else": "Caso contrário, faça isto",
"emptyBranch": "Sem degraus"
},
"repeat": {
"count": "Número de vezes",
"times": "vezes"
},
"match": {
"useRegex": "Combinar com uma expressão regular",
"useRegexHelp": "Deixe este campo em branco para que o texto corresponda exatamente ao que foi escrito.",
"invalidRegex": "Expressão regular inválida; será interpretada como texto simples."
},
"seconds": "segundos",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Temas guardados",
"scrollbackBufferLabel": "Buffer de Retrocesso",
"scrollbackMaxLines": "Número máximo de linhas guardadas no histórico",
"startupSnippetLabel": "Fragmento de inicialização",
"sudoPasswordAutoFill": "Preenchimento Automático da Palavra-passe Sudo",
"sudoPasswordAutoFillDesc": "Armazenar uma palavra-passe sudo para que as métricas do host, prompts do terminal e outros possam executar comandos com privilégios automaticamente.",
"sudoPasswordAutoFillLabel": "Preenchimento automático da palavra-passe sudo",
@@ -335,7 +334,8 @@
"history": "Histórico",
"localTerminal": "Terminal local",
"sshTools": "Ferramentas SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macros"
},
"sshTools": {
"broadcastInputPlaceholder": "Digite aqui para difundir as teclas premidas...",
@@ -363,5 +363,112 @@
"off": "Desligado",
"on": "Em",
"title": "Configurações de ligação padrão"
},
"keybindings": {
"copy": "Copiar seleção",
"paste": "Colar",
"sendControlCode": "Enviar sinal Ctrl+letra",
"sendText": "Enviar mensagem de texto",
"controlCodeLabel": "Carta",
"textLabel": "Texto a enviar",
"appendEnterLabel": "Prima Enter após enviar",
"clipboardPermissionNote": "Pode solicitar permissão para aceder à área de transferência na primeira utilização em alguns navegadores.",
"textRequired": "Introduza o texto que pretende enviar.",
"controlCodeRequired": "Introduza uma única letra para o código de controlo."
},
"macros": {
"title": "Macros",
"description": "Execute uma série de comandos guardados num terminal e reaja ao que o servidor envia de volta.",
"target": "Objetivo",
"noTerminal": "Nenhum terminal aberto",
"empty": "Ainda não existem macros.",
"emptyHint": "Crie uma macro para automatizar um fluxo de trabalho no terminal, como por exemplo, iniciar sessão, reiniciar um serviço ou responder a pedidos.",
"create": "Nova macro",
"back": "Voltar às macros",
"name": "Nome",
"namePlaceholder": "Reinicie o servidor web.",
"nameRequired": "Dê um nome à macro antes de guardar.",
"descriptionLabel": "Descrição",
"descriptionPlaceholder": "O que faz esta macro (opcional)",
"unsaved": "Alterações não guardadas",
"save": "Guardar",
"saved": "Macro salvo",
"saveFailed": "Não foi possível guardar a macro.",
"delete": "Eliminar",
"deleteTitle": "Apagar esta macro?",
"deleteBody": "\"{{name}}\" será eliminado permanentemente. Esta ação não pode ser anulada.",
"deleted": "Macro excluída",
"cancel": "Cancelar",
"run": "Correr",
"stop": "Parar",
"running": "Corrida...",
"completed": "Macro finalizado",
"failed": "A macro falhou",
"loadFailed": "Não foi possível carregar as macros",
"needTerminal": "Primeiro, abra e ligue um terminal.",
"noSteps": "Esta macro ainda não tem etapas. Adicione uma abaixo.",
"addStep": "Adicionar etapa",
"stepNumber": "Passo {{number}}",
"removeStep": "Remover etapa",
"confirmRunTitle": "Executar esta macro?",
"confirmRunBody": "\"{{name}}\" enviará comandos para {{host}}.",
"confirmRunAction": "Executar macro",
"stepTypes": {
"send": {
"label": "Enviar comando",
"help": "Digita o texto no terminal, da mesma forma que se o digitasse."
},
"wait": {
"label": "Aguarde a saída",
"help": "Aguarda até que o servidor imprima o texto que introduziu, como por exemplo um pedido de palavra-passe."
},
"delay": {
"label": "Pausa",
"help": "Aguarda um período de tempo fixo antes da etapa seguinte."
},
"if": {
"label": "Se a saída contiver",
"help": "Verifica tudo o que o servidor imprimiu até à data e, em seguida, executa uma das duas ramificações."
},
"repeat": {
"label": "Repetir",
"help": "Executa os passos internos um número definido de vezes."
}
},
"send": {
"text": "Comando ou texto",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Prima Enter após enviar",
"pressEnterHelp": "Desative esta opção para digitar texto sem o enviar."
},
"wait": {
"pattern": "Aguarde até que a saída contenha",
"patternPlaceholder": "palavra-passe:",
"timeout": "Desistir depois",
"onTimeout": "Se nunca aparecer",
"stop": "Pare a macro",
"continue": "Continue mesmo assim"
},
"delay": {
"duration": "Aguarde"
},
"ifStep": {
"pattern": "Se a saída contiver",
"patternPlaceholder": "Ativo: ativo (em execução)",
"then": "Então faça isso",
"else": "Caso contrário, faça-o",
"emptyBranch": "Sem degraus"
},
"repeat": {
"count": "Número de vezes",
"times": "vezes"
},
"match": {
"useRegex": "Combinar com uma expressão regular",
"useRegexHelp": "Deixe este campo em branco para que o texto corresponda exatamente ao que foi escrito.",
"invalidRegex": "Expressão regular inválida; será interpretada como texto simples."
},
"seconds": "segundos",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Teme salvate",
"scrollbackBufferLabel": "Buffer de derulare înapoi",
"scrollbackMaxLines": "Numărul maxim de linii păstrate în istoric",
"startupSnippetLabel": "Fragment de pornire",
"sudoPasswordAutoFill": "Completare automată parolă Sudo",
"sudoPasswordAutoFillDesc": "Stocați o parolă sudo, astfel încât metricile gazdei, prompturile terminalului și alte funcții să poată executa automat comenzi privilegiate.",
"sudoPasswordAutoFillLabel": "Completare automată parolă Sudo",
@@ -335,7 +334,8 @@
"history": "Istorie",
"localTerminal": "Terminal local",
"sshTools": "Instrumente SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macrocomenzi"
},
"sshTools": {
"broadcastInputPlaceholder": "Tastați aici pentru a difuza apăsările de taste...",
@@ -363,5 +363,112 @@
"off": "Dezactivat",
"on": "Pe",
"title": "Setări implicite ale conexiunii"
},
"keybindings": {
"copy": "Copiați selecția",
"paste": "Pastă",
"sendControlCode": "Trimite semnal Ctrl+literă",
"sendText": "Trimiteți mesaj text",
"controlCodeLabel": "Scrisoare",
"textLabel": "Text de trimis",
"appendEnterLabel": "Apăsați Enter după trimitere",
"clipboardPermissionNote": "În unele browsere, este posibil să solicite permisiunea pentru clipboard la prima utilizare.",
"textRequired": "Introduceți textul de trimis",
"controlCodeRequired": "Introduceți o singură literă pentru codul de control"
},
"macros": {
"title": "Macrocomenzi",
"description": "Executați o serie de comenzi salvate într-un terminal și reacționați la ceea ce serverul trimite înapoi.",
"target": "Ţintă",
"noTerminal": "Niciun terminal deschis",
"empty": "Încă nu există macrocomenzi",
"emptyHint": "Creați o macrocomandă pentru a automatiza un flux de lucru în terminal, cum ar fi conectarea, repornirea unui serviciu sau răspunsul la solicitări.",
"create": "Macrocomandă nouă",
"back": "Înapoi la macrocomenzi",
"name": "Nume",
"namePlaceholder": "Reporniți serverul web",
"nameRequired": "Dați un nume macrocomenzii înainte de salvare",
"descriptionLabel": "Descriere",
"descriptionPlaceholder": "Ce face această macrocomandă (opțional)",
"unsaved": "Modificări nesalvate",
"save": "Salva",
"saved": "Macrocomandă salvată",
"saveFailed": "Nu s-a putut salva macrocomanda",
"delete": "Şterge",
"deleteTitle": "Ștergeți această macrocomandă?",
"deleteBody": "„{{name}}” va fi șters definitiv. Această acțiune nu poate fi anulată.",
"deleted": "Macrocomandă ștearsă",
"cancel": "Anula",
"run": "Aleargă",
"stop": "Stop",
"running": "Funcţionare...",
"completed": "Macrocomandă finalizată",
"failed": "Macrocomanda a eșuat",
"loadFailed": "Nu s-au putut încărca macrocomenzile",
"needTerminal": "Deschideți și conectați mai întâi un terminal",
"noSteps": "Această macrocomandă nu are încă pași. Adăugați unul mai jos.",
"addStep": "Adăugați un pas",
"stepNumber": "Pasul {{number}}",
"removeStep": "Eliminați pasul",
"confirmRunTitle": "Rulați această macrocomandă?",
"confirmRunBody": "„{{name}}” va trimite comenzi către {{host}}.",
"confirmRunAction": "Executare macrocomandă",
"stepTypes": {
"send": {
"label": "Trimiteți comanda",
"help": "Introduce text în terminal, la fel ca și cum l-ai fi tastat."
},
"wait": {
"label": "Așteptați ieșirea",
"help": "Se întrerupe până când serverul afișează textul pe care îl denumiți, ca o solicitare de parolă."
},
"delay": {
"label": "Pauză",
"help": "Așteaptă o perioadă fixă de timp înainte de pasul următor."
},
"if": {
"label": "Dacă ieșirea conține",
"help": "Verifică tot ce a imprimat serverul până acum, apoi rulează una dintre cele două ramificații."
},
"repeat": {
"label": "Repeta",
"help": "Execută pașii din interiorul său de un număr stabilit de ori."
}
},
"send": {
"text": "Comandă sau text",
"textPlaceholder": "repornirea systemctl a nginx",
"pressEnter": "Apăsați Enter după trimitere",
"pressEnterHelp": "Dezactivați această opțiune pentru a introduce text fără a-l trimite."
},
"wait": {
"pattern": "Așteptați până când ieșirea conține",
"patternPlaceholder": "parolă:",
"timeout": "Renunță după",
"onTimeout": "Dacă nu apare niciodată",
"stop": "Opriți macrocomanda",
"continue": "Continuă oricum"
},
"delay": {
"duration": "Așteptați"
},
"ifStep": {
"pattern": "Dacă ieșirea conține",
"patternPlaceholder": "Activ: activ (alergând)",
"then": "Apoi fă asta",
"else": "Altfel, fă asta",
"emptyBranch": "Fără trepte"
},
"repeat": {
"count": "Numărul de ori",
"times": "ori"
},
"match": {
"useRegex": "Potrivire cu o expresie regulată",
"useRegexHelp": "Omiteți această opțiune pentru a corespunde exact textului așa cum este scris.",
"invalidRegex": "Nu este o expresie regulată validă, va fi găsită ca text simplu."
},
"seconds": "secunde",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Сохраненные темы",
"scrollbackBufferLabel": "Буфер прокрутки",
"scrollbackMaxLines": "Максимальное количество строк, сохраняемых в истории",
"startupSnippetLabel": "Фрагмент запуска",
"sudoPasswordAutoFill": "Автозаполнение пароля sudo",
"sudoPasswordAutoFillDesc": "Хранить пароль sudo, чтобы метрики хоста, запросы терминала и другие могли автоматически выполнять привилегированные команды.",
"sudoPasswordAutoFillLabel": "Автозаполнение пароля sudo",
@@ -335,7 +334,8 @@
"history": "История",
"localTerminal": "Локальный терминал",
"sshTools": "Инструменты SSH",
"terminal": "Терминал"
"terminal": "Терминал",
"macros": "Макросы"
},
"sshTools": {
"broadcastInputPlaceholder": "Введите текст для трансляции нажатий...",
@@ -363,5 +363,112 @@
"off": "Выключенный",
"on": "На",
"title": "Настройки подключения по умолчанию"
},
"keybindings": {
"copy": "Скопировать выделенный фрагмент",
"paste": "Вставить",
"sendControlCode": "Отправить сигнал Ctrl+буква",
"sendText": "Отправить текст",
"controlCodeLabel": "Письмо",
"textLabel": "Текст для отправки",
"appendEnterLabel": "Нажмите Enter после отправки",
"clipboardPermissionNote": "В некоторых браузерах при первом использовании может запрашиваться разрешение на использование буфера обмена.",
"textRequired": "Введите текст для отправки",
"controlCodeRequired": "Введите одну букву в качестве управляющего кода."
},
"macros": {
"title": "Макросы",
"description": "Выполните сохраненную последовательность команд в терминале и отреагируйте на то, что сервер отправит вам в ответ.",
"target": "Цель",
"noTerminal": "Терминал не открыт",
"empty": "Макросы пока отсутствуют",
"emptyHint": "Создайте макрос для автоматизации действий в терминале, таких как вход в систему, перезапуск службы или ответ на запросы.",
"create": "Новый макрос",
"back": "Вернуться к макроэлементам",
"name": "Имя",
"namePlaceholder": "Перезапустите веб-сервер",
"nameRequired": "Перед сохранением дайте макросу имя.",
"descriptionLabel": "Описание",
"descriptionPlaceholder": "Что делает этот макрос (необязательно)",
"unsaved": "Несохраненные изменения",
"save": "Сохранять",
"saved": "Макрос сохранен",
"saveFailed": "Не удалось сохранить макрос.",
"delete": "Удалить",
"deleteTitle": "Удалить этот макрос?",
"deleteBody": "\"{{name}}\" будет удалено навсегда. Этого нельзя будет отменить.",
"deleted": "Макрос удален",
"cancel": "Отмена",
"run": "Бегать",
"stop": "Останавливаться",
"running": "Бег...",
"completed": "Макро завершен",
"failed": "Макрос не сработал",
"loadFailed": "Не удалось загрузить макросы.",
"needTerminal": "Сначала откройте и подключите терминал.",
"noSteps": "В этом макросе пока нет шагов. Добавьте один ниже.",
"addStep": "Добавить шаг",
"stepNumber": "Шаг {{number}}",
"removeStep": "Удалить шаг",
"confirmRunTitle": "Запустить этот макрос?",
"confirmRunBody": "\"{{name}}\" будет отправлять команды {{host}}.",
"confirmRunAction": "Запустить макрос",
"stepTypes": {
"send": {
"label": "Отправить команду",
"help": "Вводит текст в терминал так же, как если бы вы его ввели вручную."
},
"wait": {
"label": "Дождитесь вывода",
"help": "Приостанавливается до тех пор, пока сервер не выведет указанный вами текст, как запрос пароля."
},
"delay": {
"label": "Пауза",
"help": "Перед следующим шагом выполняется ожидание в течение заданного промежутка времени."
},
"if": {
"label": "Если вывод содержит",
"help": "Проверяет все данные, выведенные сервером на экран, а затем запускает одну из двух ветвей выполнения."
},
"repeat": {
"label": "Повторить",
"help": "Выполняет все шаги, входящие в его состав, заданное количество раз."
}
},
"send": {
"text": "Команда или текст",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Нажмите Enter после отправки",
"pressEnterHelp": "Отключите эту функцию, чтобы вводить текст без отправки."
},
"wait": {
"pattern": "Дождитесь, пока на выходе не появится",
"patternPlaceholder": "пароль:",
"timeout": "Сдаться после",
"onTimeout": "Если оно никогда не появится",
"stop": "Остановите макрос",
"continue": "Продолжайте в любом случае."
},
"delay": {
"duration": "Подождите"
},
"ifStep": {
"pattern": "Если в выходных данных содержится",
"patternPlaceholder": "Активный: активен (работает)",
"then": "Затем сделайте это.",
"else": "В противном случае сделайте это.",
"emptyBranch": "Без ступенек"
},
"repeat": {
"count": "Количество раз",
"times": "времена"
},
"match": {
"useRegex": "Сопоставление с использованием регулярного выражения",
"useRegexHelp": "Оставьте этот параметр отключенным, чтобы текст точно соответствовал написанному.",
"invalidRegex": "Не является допустимым регулярным выражением, будет сопоставлено как обычный текст."
},
"seconds": "секунд",
"secondsShort": "{{count}}с"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Сачуване теме",
"scrollbackBufferLabel": "Бафер за померање уназад",
"scrollbackMaxLines": "Максималан број линија сачуваних у историји",
"startupSnippetLabel": "Исечак за покретање",
"sudoPasswordAutoFill": "Аутоматско попуњавање лозинке за Судо",
"sudoPasswordAutoFillDesc": "Сачувајте лозинку за sudo како би метрике хоста, упити терминала и други могли аутоматски да покрећу привилеговане команде.",
"sudoPasswordAutoFillLabel": "Аутоматско попуњавање лозинке за Sudo",
@@ -335,7 +334,8 @@
"history": "Историја",
"localTerminal": "Локални терминал",
"sshTools": "SSH алати",
"terminal": "Терминал"
"terminal": "Терминал",
"macros": "Макрои"
},
"sshTools": {
"broadcastInputPlaceholder": "Куцајте овде да бисте емитовали притиске тастера...",
@@ -363,5 +363,112 @@
"off": "Искључено",
"on": "Укључено",
"title": "Подразумеване вредности везе"
},
"keybindings": {
"copy": "Копирај избор",
"paste": "Залепи",
"sendControlCode": "Пошаљи сигнал Ctrl+слово",
"sendText": "Пошаљи текст",
"controlCodeLabel": "Писмо",
"textLabel": "Текст за слање",
"appendEnterLabel": "Притисните Ентер након слања",
"clipboardPermissionNote": "Може затражити дозволу за међуспремник при првој употреби у неким прегледачима.",
"textRequired": "Унесите текст за слање",
"controlCodeRequired": "Унесите једно слово за контролни код"
},
"macros": {
"title": "Макрои",
"description": "Покрените сачувану серију команди у терминалу и реагујте на оно што сервер пошаље назад.",
"target": "Циљ",
"noTerminal": "Ниједан терминал није отворен",
"empty": "Још нема макроа",
"emptyHint": "Направите макро за аутоматизацију тока рада терминала, као што је пријављивање, поновно покретање сервиса или одговарање на упите.",
"create": "Нови макро",
"back": "Назад на макрое",
"name": "Име",
"namePlaceholder": "Поново покрените веб сервер",
"nameRequired": "Дајте макроу име пре чувања",
"descriptionLabel": "Опис",
"descriptionPlaceholder": "Шта овај макро ради (опционо)",
"unsaved": "Несачуване промене",
"save": "Сачувај",
"saved": "Макро је сачуван",
"saveFailed": "Није могуће сачувати макро",
"delete": "Обриши",
"deleteTitle": "Да ли желите да обришете овај макро?",
"deleteBody": "„{{name}}“ ће бити трајно обрисано. Ова радња се не може поништити.",
"deleted": "Макро је обрисан",
"cancel": "Откажи",
"run": "Трчи",
"stop": "Заустави",
"running": "Трчи се...",
"completed": "Макро је завршен",
"failed": "Макро није успео",
"loadFailed": "Није могуће учитати макрое",
"needTerminal": "Прво отворите и повежите терминал",
"noSteps": "Овај макро још нема кораке. Додајте један испод.",
"addStep": "Додај корак",
"stepNumber": "Корак {{number}}",
"removeStep": "Уклони корак",
"confirmRunTitle": "Покрени овај макро?",
"confirmRunBody": "„{{name}}“ ће послати команде на {{host}}.",
"confirmRunAction": "Покрени макро",
"stepTypes": {
"send": {
"label": "Пошаљи команду",
"help": "Уноси текст у терминал, исто као да сте га откуцали."
},
"wait": {
"label": "Сачекајте излаз",
"help": "Паузира док сервер не одштампа текст који наведете, као што је захтев за лозинку."
},
"delay": {
"label": "Пауза",
"help": "Чека одређено време пре следећег корака."
},
"if": {
"label": "Ако излаз садржи",
"help": "Проверава све што је сервер до сада одштампао, а затим покреће једну од две гране."
},
"repeat": {
"label": "Понови",
"help": "Покреће кораке унутар њега одређени број пута."
}
},
"send": {
"text": "Команда или текст",
"textPlaceholder": "системцтл рестартовање нгинкса",
"pressEnter": "Притисните Ентер након слања",
"pressEnterHelp": "Искључите ово да бисте куцали текст без његовог слања."
},
"wait": {
"pattern": "Сачекајте док излаз не садржи",
"patternPlaceholder": "лозинка:",
"timeout": "Одустани после",
"onTimeout": "Ако се никада не појави",
"stop": "Зауставите макро",
"continue": "Настави ипак"
},
"delay": {
"duration": "Сачекајте"
},
"ifStep": {
"pattern": "Ако излаз садржи",
"patternPlaceholder": "Активно: активно (покреће се)",
"then": "Онда уради ово",
"else": "У супротном, урадите ово",
"emptyBranch": "Без степеница"
},
"repeat": {
"count": "Број пута",
"times": "пута"
},
"match": {
"useRegex": "Упари се са регуларним изразом",
"useRegexHelp": "Оставите ово искључено да би текст тачно одговарао написаном тексту.",
"invalidRegex": "Није важећи регуларни израз, биће упарен као обичан текст"
},
"seconds": "секунде",
"secondsShort": "{{count}}с"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Sparade teman",
"scrollbackBufferLabel": "Scrollback-buffert",
"scrollbackMaxLines": "Maximalt antal rader som sparas i historiken",
"startupSnippetLabel": "Startkodavsnitt",
"sudoPasswordAutoFill": "Sudo-lösenordsautomatik",
"sudoPasswordAutoFillDesc": "Lagra ett sudo-lösenord så att värdmätvärden, terminalprompter och annat kan köra privilegierade kommandon automatiskt.",
"sudoPasswordAutoFillLabel": "Sudo-lösenordsautomatik",
@@ -335,7 +334,8 @@
"history": "Historia",
"localTerminal": "Lokal terminal",
"sshTools": "SSH-verktyg",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makron"
},
"sshTools": {
"broadcastInputPlaceholder": "Skriv här för att sända tangenttryckningar...",
@@ -363,5 +363,112 @@
"off": "Av",
"on": "På",
"title": "Standardinställningar för anslutning"
},
"keybindings": {
"copy": "Kopiera val",
"paste": "Klistra",
"sendControlCode": "Skicka Ctrl+bokstavssignal",
"sendText": "Skicka sms",
"controlCodeLabel": "Brev",
"textLabel": "SMS att skicka",
"appendEnterLabel": "Tryck Enter efter att du skickat",
"clipboardPermissionNote": "Kan be om behörighet för urklipp vid första användningen i vissa webbläsare.",
"textRequired": "Ange texten som ska skickas",
"controlCodeRequired": "Ange en enda bokstav för kontrollkoden"
},
"macros": {
"title": "Makron",
"description": "Kör en sparad serie kommandon i en terminal och reagera på vad servern skickar tillbaka.",
"target": "Mål",
"noTerminal": "Ingen terminal öppen",
"empty": "Inga makron ännu",
"emptyHint": "Skapa ett makro för att automatisera ett terminalflöde, som att logga in, starta om en tjänst eller svara på frågor.",
"create": "Nytt makro",
"back": "Tillbaka till makron",
"name": "Namn",
"namePlaceholder": "Starta om webbservern",
"nameRequired": "Ge makrot ett namn innan du sparar",
"descriptionLabel": "Beskrivning",
"descriptionPlaceholder": "Vad detta makro gör (valfritt)",
"unsaved": "Osparade ändringar",
"save": "Spara",
"saved": "Makrot sparat",
"saveFailed": "Kunde inte spara makrot",
"delete": "Radera",
"deleteTitle": "Ta bort det här makrot?",
"deleteBody": "\"{{name}}\" kommer att raderas permanent. Detta kan inte ångras.",
"deleted": "Makro borttaget",
"cancel": "Avboka",
"run": "Sikt",
"stop": "Stopp",
"running": "Spring...",
"completed": "Makro klart",
"failed": "Makrot misslyckades",
"loadFailed": "Kunde inte läsa in makron",
"needTerminal": "Öppna och anslut en terminal först",
"noSteps": "Det här makrot har inga steg ännu. Lägg till ett nedan.",
"addStep": "Lägg till steg",
"stepNumber": "Steg {{number}}",
"removeStep": "Ta bort steg",
"confirmRunTitle": "Köra det här makrot?",
"confirmRunBody": "\"{{name}}\" skickar kommandon till {{host}}.",
"confirmRunAction": "Kör makro",
"stepTypes": {
"send": {
"label": "Skicka kommando",
"help": "Skriver text i terminalen, på samma sätt som om du hade skrivit den."
},
"wait": {
"label": "Vänta på utdata",
"help": "Pausar tills servern skriver ut texten du anger, som en lösenordsfråga."
},
"delay": {
"label": "Paus",
"help": "Väntar en fastställd tid innan nästa steg."
},
"if": {
"label": "Om utdata innehåller",
"help": "Kontrollerar allt som servern har skrivit ut hittills och kör sedan en av två grenar."
},
"repeat": {
"label": "Upprepa",
"help": "Kör stegen inuti ett visst antal gånger."
}
},
"send": {
"text": "Kommando eller text",
"textPlaceholder": "systemctl starta om nginx",
"pressEnter": "Tryck Enter efter att du skickat",
"pressEnterHelp": "Stäng av detta för att skriva text utan att skicka in den."
},
"wait": {
"pattern": "Vänta tills utdata innehåller",
"patternPlaceholder": "lösenord:",
"timeout": "Ge upp efteråt",
"onTimeout": "Om det aldrig dyker upp",
"stop": "Stoppa makrot",
"continue": "Fortsätt ändå"
},
"delay": {
"duration": "Vänta på"
},
"ifStep": {
"pattern": "Om utdata innehåller",
"patternPlaceholder": "Aktiv: aktiv (löper)",
"then": "Gör sedan detta",
"else": "Annars gör så här",
"emptyBranch": "Inga steg"
},
"repeat": {
"count": "Antal gånger",
"times": "gånger"
},
"match": {
"useRegex": "Matcha med ett reguljärt uttryck",
"useRegexHelp": "Lämna detta av för att texten ska se exakt ut som den är skriven.",
"invalidRegex": "Inte ett giltigt reguljärt uttryck, det kommer att matchas som vanlig text"
},
"seconds": "sekunder",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "ธีมที่บันทึกไว้",
"scrollbackBufferLabel": "บัฟเฟอร์การเลื่อนกลับ",
"scrollbackMaxLines": "จำนวนบรรทัดสูงสุดที่เก็บไว้ในประวัติ",
"startupSnippetLabel": "ตัวอย่างโค้ดสำหรับเริ่มต้น",
"sudoPasswordAutoFill": "การกรอกรหัสผ่านอัตโนมัติของ Sudo",
"sudoPasswordAutoFillDesc": "บันทึกรหัสผ่าน sudo เพื่อให้โปรแกรมตรวจสอบข้อมูลโฮสต์ ข้อความแจ้งเตือนในเทอร์มินัล และโปรแกรมอื่นๆ สามารถเรียกใช้คำสั่งที่มีสิทธิ์พิเศษได้โดยอัตโนมัติ",
"sudoPasswordAutoFillLabel": "การกรอกรหัสผ่านอัตโนมัติของ Sudo",
@@ -335,7 +334,8 @@
"history": "ประวัติศาสตร์",
"localTerminal": "สถานีปลายทางท้องถิ่น",
"sshTools": "เครื่องมือ SSH",
"terminal": "เทอร์มินัล"
"terminal": "เทอร์มินัล",
"macros": "มาโคร"
},
"sshTools": {
"broadcastInputPlaceholder": "พิมพ์ที่นี่เพื่อถ่ายทอดการกดแป้นพิมพ์...",
@@ -363,5 +363,112 @@
"off": "ปิด",
"on": "บน",
"title": "ค่าเริ่มต้นการเชื่อมต่อ"
},
"keybindings": {
"copy": "คัดลอกการเลือก",
"paste": "แปะ",
"sendControlCode": "ส่งสัญญาณ Ctrl+ตัวอักษร",
"sendText": "ส่งข้อความ",
"controlCodeLabel": "จดหมาย",
"textLabel": "ข้อความที่จะส่ง",
"appendEnterLabel": "กด Enter หลังจากส่งแล้ว",
"clipboardPermissionNote": "ในบางเบราว์เซอร์ อาจขออนุญาตคัดลอกและวางข้อมูลลงในคลิปบอร์ดเมื่อใช้งานครั้งแรก",
"textRequired": "ป้อนข้อความที่จะส่ง",
"controlCodeRequired": "ป้อนตัวอักษรตัวเดียวสำหรับรหัสควบคุม"
},
"macros": {
"title": "มาโคร",
"description": "เรียกใช้ชุดคำสั่งที่บันทึกไว้ในเทอร์มินัล แล้วตอบสนองต่อสิ่งที่เซิร์ฟเวอร์ส่งกลับมา",
"target": "เป้า",
"noTerminal": "ไม่มีเทอร์มินัลเปิดอยู่",
"empty": "ยังไม่มีมาโคร",
"emptyHint": "สร้างมาโครเพื่อทำให้กระบวนการทำงานบนเทอร์มินัลเป็นไปโดยอัตโนมัติ เช่น การล็อกอิน การรีสตาร์ทบริการ หรือการตอบคำถามต่างๆ",
"create": "มาโครใหม่",
"back": "กลับไปที่มาโคร",
"name": "ชื่อ",
"namePlaceholder": "รีสตาร์ทเว็บเซิร์ฟเวอร์",
"nameRequired": "ตั้งชื่อมาโครก่อนบันทึก",
"descriptionLabel": "คำอธิบาย",
"descriptionPlaceholder": "มาโครนี้ทำอะไร (ไม่บังคับ)",
"unsaved": "การเปลี่ยนแปลงที่ยังไม่ได้บันทึก",
"save": "บันทึก",
"saved": "บันทึกมาโครแล้ว",
"saveFailed": "ไม่สามารถบันทึกมาโครได้",
"delete": "ลบ",
"deleteTitle": "ลบมาโครนี้?",
"deleteBody": "\"{{name}}\" จะถูกลบอย่างถาวร ไม่สามารถกู้คืนได้",
"deleted": "มาโครถูกลบแล้ว",
"cancel": "ยกเลิก",
"run": "วิ่ง",
"stop": "หยุด",
"running": "วิ่ง...",
"completed": "มาโครเสร็จสิ้น",
"failed": "มาโครล้มเหลว",
"loadFailed": "ไม่สามารถโหลดมาโครได้",
"needTerminal": "เปิดและเชื่อมต่อเทอร์มินัลก่อน",
"noSteps": "มาโครนี้ยังไม่มีขั้นตอนใดๆ เพิ่มขั้นตอนด้านล่าง",
"addStep": "เพิ่มขั้นตอน",
"stepNumber": "ขั้นตอน {{number}}",
"removeStep": "ลบขั้นตอน",
"confirmRunTitle": "เรียกใช้มาโครนี้หรือไม่?",
"confirmRunBody": "\"{{name}}\" จะส่งคำสั่งไปยัง {{host}}",
"confirmRunAction": "เรียกใช้มาโคร",
"stepTypes": {
"send": {
"label": "ส่งคำสั่ง",
"help": "พิมพ์ข้อความลงในเทอร์มินัล เหมือนกับที่คุณพิมพ์ข้อความนั้นด้วยตนเอง"
},
"wait": {
"label": "รอผลลัพธ์",
"help": "หยุดรอจนกว่าเซิร์ฟเวอร์จะแสดงข้อความที่คุณระบุ เช่นเดียวกับการขอให้ป้อนรหัสผ่าน"
},
"delay": {
"label": "หยุดชั่วคราว",
"help": "รอเป็นระยะเวลาที่กำหนดก่อนดำเนินการขั้นตอนต่อไป"
},
"if": {
"label": "หากผลลัพธ์ประกอบด้วย",
"help": "ตรวจสอบทุกอย่างที่เซิร์ฟเวอร์ได้พิมพ์ออกมาแล้ว จากนั้นจึงเลือกดำเนินการตามหนึ่งในสองทางเลือก"
},
"repeat": {
"label": "ทำซ้ำ",
"help": "ดำเนินการตามขั้นตอนภายในตามจำนวนครั้งที่กำหนด"
}
},
"send": {
"text": "คำสั่งหรือข้อความ",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "กด Enter หลังจากส่งแล้ว",
"pressEnterHelp": "ปิดตัวเลือกนี้เพื่อพิมพ์ข้อความโดยไม่ต้องกดส่ง"
},
"wait": {
"pattern": "รอจนกว่าผลลัพธ์จะประกอบด้วย",
"patternPlaceholder": "รหัสผ่าน:",
"timeout": "ยอมแพ้หลังจาก",
"onTimeout": "หากมันไม่ปรากฏเลย",
"stop": "หยุดมาโคร",
"continue": "อย่าหยุดแค่นี้"
},
"delay": {
"duration": "รอสักครู่"
},
"ifStep": {
"pattern": "หากผลลัพธ์ประกอบด้วย",
"patternPlaceholder": "สถานะ: กำลังทำกิจกรรม (วิ่ง)",
"then": "จากนั้นให้ทำแบบนี้",
"else": "หรือทำแบบนี้ก็ได้",
"emptyBranch": "ไม่มีขั้นตอน"
},
"repeat": {
"count": "จำนวนครั้ง",
"times": "ครั้ง"
},
"match": {
"useRegex": "จับคู่ด้วยนิพจน์ปกติ",
"useRegexHelp": "ไม่ต้องใส่ส่วนนี้เพื่อให้ตรงกับข้อความที่เขียนไว้ทุกประการ",
"invalidRegex": "ไม่ใช่รูปแบบนิพจน์ปกติที่ถูกต้อง ระบบจะถือว่าเป็นข้อความธรรมดา"
},
"seconds": "วินาที",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Kaydedilen Temalar",
"scrollbackBufferLabel": "Kaydırma Arabelleği",
"scrollbackMaxLines": "Geçmişte saklanan maksimum satır sayısı",
"startupSnippetLabel": "Başlangıç Betiği",
"sudoPasswordAutoFill": "Sudo Parolası Otomatik Doldurma",
"sudoPasswordAutoFillDesc": "Sudo parolasını saklayın; böylece ana bilgisayar metrikleri, terminal istemleri ve diğer bileşenler ayrıcalıklı komutları otomatik olarak çalıştırabilir.",
"sudoPasswordAutoFillLabel": "Sudo Parolası Otomatik Doldurma",
@@ -335,7 +334,8 @@
"history": "Geçmiş",
"localTerminal": "Yerel Terminal",
"sshTools": "SSH Araçları",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Makrolar"
},
"sshTools": {
"broadcastInputPlaceholder": "Tuş vuruşlarını yayınlamak için buraya yazın...",
@@ -363,5 +363,112 @@
"off": "Kapalı",
"on": "Açık",
"title": "Bağlantı Varsayılanları"
},
"keybindings": {
"copy": "Seçimi kopyala",
"paste": "Yapıştır",
"sendControlCode": "Ctrl+harf sinyali gönder",
"sendText": "Mesaj gönder",
"controlCodeLabel": "Mektup",
"textLabel": "Gönderilecek metin",
"appendEnterLabel": "Gönderdikten sonra Enter tuşuna basın.",
"clipboardPermissionNote": "Bazı tarayıcılarda ilk kullanımda panoya kopyalama izni isteyebilir.",
"textRequired": "Göndermek istediğiniz metni girin.",
"controlCodeRequired": "Kontrol kodu için tek bir harf girin."
},
"macros": {
"title": "Makrolar",
"description": "Önceden kaydedilmiş bir komut dizisini terminalde çalıştırın ve sunucunun geri gönderdiği yanıtlara göre hareket edin.",
"target": "Hedef",
"noTerminal": "Açık terminal yok.",
"empty": "Henüz makro yok",
"emptyHint": "Giriş yapma, bir hizmeti yeniden başlatma veya komut istemlerine yanıt verme gibi terminal işlemlerini otomatikleştirmek için bir makro oluşturun.",
"create": "Yeni makro",
"back": "Makrolara geri dön",
"name": "İsim",
"namePlaceholder": "Web sunucusunu yeniden başlatın.",
"nameRequired": "Kaydetmeden önce makroya bir ad verin.",
"descriptionLabel": "Tanım",
"descriptionPlaceholder": "Bu makronun ne yaptığı (isteğe bağlı)",
"unsaved": "Kaydedilmemiş değişiklikler",
"save": "Kaydetmek",
"saved": "Makro kaydedildi.",
"saveFailed": "Makro kaydedilemedi.",
"delete": "Silmek",
"deleteTitle": "Bu makroyu silmek ister misiniz?",
"deleteBody": "\"{{name}}\" kalıcı olarak silinecektir. Bu işlem geri alınamaz.",
"deleted": "Makro silindi",
"cancel": "İptal etmek",
"run": "Koşmak",
"stop": "Durmak",
"running": "Koşma...",
"completed": "Makro tamamlandı",
"failed": "Makro başarısız oldu",
"loadFailed": "Makrolar yüklenemedi.",
"needTerminal": "Öncelikle bir terminal açın ve bağlayın.",
"noSteps": "Bu makronun henüz hiçbir adımı yok. Aşağıya bir adım ekleyin.",
"addStep": "Adım ekle",
"stepNumber": "Adım {{number}}",
"removeStep": "Adımı kaldır",
"confirmRunTitle": "Bu makroyu çalıştırayım mı?",
"confirmRunBody": "\"{{name}}\" {{host}} adresine komutlar gönderecektir.",
"confirmRunAction": "Makroyu çalıştır",
"stepTypes": {
"send": {
"label": "Komut gönder",
"help": "Terminale, sanki siz yazmışsınız gibi metin yazar."
},
"wait": {
"label": "Çıktıyı bekleyin",
"help": "Sunucu, belirttiğiniz metni (örneğin parola istemi gibi) yazdırana kadar bekler."
},
"delay": {
"label": "Duraklat",
"help": "Bir sonraki adıma geçmeden önce belirli bir süre bekler."
},
"if": {
"label": "Çıktı içeriyorsa",
"help": "Sunucunun şu ana kadar yazdırdığı her şeyi kontrol eder, ardından iki daldan birini çalıştırır."
},
"repeat": {
"label": "Tekrarlamak",
"help": "İçindeki adımları belirtilen sayıda tekrarlar."
}
},
"send": {
"text": "Komut veya metin",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Gönderdikten sonra Enter tuşuna basın.",
"pressEnterHelp": "Göndermeden metin yazmak için bunu kapatın."
},
"wait": {
"pattern": "Çıktıyı oluşturana kadar bekleyin.",
"patternPlaceholder": "şifre:",
"timeout": "Vazgeçtikten sonra",
"onTimeout": "Eğer hiç ortaya çıkmazsa",
"stop": "Makroyu durdur",
"continue": "Yine de devam edin."
},
"delay": {
"duration": "Bekleyin"
},
"ifStep": {
"pattern": "Çıktı şunları içeriyorsa:",
"patternPlaceholder": "Aktif: aktif (koşmak)",
"then": "O halde şunu yapın",
"else": "Aksi takdirde şunu yapın",
"emptyBranch": "Hiçbir adım yok"
},
"repeat": {
"count": "Sayısı",
"times": "kez"
},
"match": {
"useRegex": "Düzenli ifadeyle eşleştirin",
"useRegexHelp": "Metnin aynen yazıldığı gibi kalması için bunu atlayın.",
"invalidRegex": "Geçerli bir normal ifade değil, düz metin olarak eşleştirilecektir."
},
"seconds": "saniyeler",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Збережені теми",
"scrollbackBufferLabel": "Буфер прокручування",
"scrollbackMaxLines": "Максимальна кількість рядків, що зберігаються в історії",
"startupSnippetLabel": "Фрагмент запуску",
"sudoPasswordAutoFill": "Автозаповнення пароля Sudo",
"sudoPasswordAutoFillDesc": "Зберігайте пароль sudo, щоб метрики хоста, запити терміналу та інші могли автоматично запускати привілейовані команди.",
"sudoPasswordAutoFillLabel": "Автозаповнення пароля Sudo",
@@ -335,7 +334,8 @@
"history": "Історія",
"localTerminal": "Місцевий термінал",
"sshTools": "SSH-інструменти",
"terminal": "Термінал"
"terminal": "Термінал",
"macros": "Макроси"
},
"sshTools": {
"broadcastInputPlaceholder": "Введіть тут, щоб транслювати натискання клавіш...",
@@ -363,5 +363,112 @@
"off": "Вимкнено",
"on": "Увімкнено",
"title": "Налаштування підключення за замовчуванням"
},
"keybindings": {
"copy": "Копіювати виділення",
"paste": "Вставити",
"sendControlCode": "Надіслати сигнал Ctrl+літера",
"sendText": "Надіслати текст",
"controlCodeLabel": "Лист",
"textLabel": "Текст для надсилання",
"appendEnterLabel": "Натисніть Enter після відправлення",
"clipboardPermissionNote": "У деяких браузерах може запитуватися дозвіл на використання буфера обміну під час першого використання.",
"textRequired": "Введіть текст для надсилання",
"controlCodeRequired": "Введіть одну літеру для контрольного коду"
},
"macros": {
"title": "Макроси",
"description": "Виконайте збережену серію команд у терміналі та відреагуйте на те, що надсилає сервер.",
"target": "Ціль",
"noTerminal": "Термінал не відкритий",
"empty": "Поки що немає макросів",
"emptyHint": "Створіть макрос для автоматизації процесу роботи термінала, такого як вхід у систему, перезапуск служби або відповідь на запити.",
"create": "Новий макрос",
"back": "Повернутися до макросів",
"name": "Ім'я",
"namePlaceholder": "Перезапустіть веб-сервер",
"nameRequired": "Введіть ім'я макросу перед збереженням",
"descriptionLabel": "Опис",
"descriptionPlaceholder": "Що робить цей макрос (необов'язково)",
"unsaved": "Незбережені зміни",
"save": "Зберегти",
"saved": "Макрос збережено",
"saveFailed": "Не вдалося зберегти макрос",
"delete": "Видалити",
"deleteTitle": "Видалити цей макрос?",
"deleteBody": "«{{name}}» буде видалено назавжди. Цю дію неможливо скасувати.",
"deleted": "Макрос видалено",
"cancel": "Скасувати",
"run": "Бігти",
"stop": "СТІЙ",
"running": "Біг...",
"completed": "Макрос завершено",
"failed": "Помилка макросу",
"loadFailed": "Не вдалося завантажити макроси",
"needTerminal": "Спочатку відкрийте та підключіть термінал",
"noSteps": "Цей макрос ще не має кроків. Додайте один нижче.",
"addStep": "Додати крок",
"stepNumber": "Крок {{number}}",
"removeStep": "Видалити крок",
"confirmRunTitle": "Запустити цей макрос?",
"confirmRunBody": "\"{{name}}\" надсилатиме команди до {{host}}.",
"confirmRunAction": "Запустити макрос",
"stepTypes": {
"send": {
"label": "Надіслати команду",
"help": "Вводить текст у термінал так само, як і надруковано."
},
"wait": {
"label": "Зачекайте на вивід",
"help": "Призупиняється, доки сервер не надрукує вказаний вами текст, наприклад, запит на пароль."
},
"delay": {
"label": "Пауза",
"help": "Очікує фіксований проміжок часу перед наступним кроком."
},
"if": {
"label": "Якщо вихід містить",
"help": "Перевіряє все, що сервер надрукував до цього часу, а потім запускає одну з двох гілок."
},
"repeat": {
"label": "Повторити",
"help": "Виконує кроки всередині нього задану кількість разів."
}
},
"send": {
"text": "Команда або текст",
"textPlaceholder": "перезапуск systemctl nginx",
"pressEnter": "Натисніть Enter після відправлення",
"pressEnterHelp": "Вимкніть цю функцію, щоб вводити текст, не надсилаючи його."
},
"wait": {
"pattern": "Зачекайте, поки вихідні дані міститимуть",
"patternPlaceholder": "пароль:",
"timeout": "Здайся після",
"onTimeout": "Якщо воно ніколи не з'явиться",
"stop": "Зупинити макрос",
"continue": "Продовжуй у будь-якому разі"
},
"delay": {
"duration": "Зачекайте"
},
"ifStep": {
"pattern": "Якщо вихід містить",
"patternPlaceholder": "Активний: активний (працює)",
"then": "Тоді зробіть це",
"else": "Інакше зробіть це",
"emptyBranch": "Без сходинок"
},
"repeat": {
"count": "Кількість разів",
"times": "рази"
},
"match": {
"useRegex": "Зіставлення з регулярним виразом",
"useRegexHelp": "Не виключайте це, щоб текст точно відповідав написаному.",
"invalidRegex": "Недійсний регулярний вираз, буде знайдено звичайний текст"
},
"seconds": "секунди",
"secondsShort": "{{count}}с"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "Chủ đề đã lưu",
"scrollbackBufferLabel": "Bộ đệm cuộn ngược",
"scrollbackMaxLines": "Số dòng tối đa được giữ trong lịch sử",
"startupSnippetLabel": "Đoạn mã khởi động",
"sudoPasswordAutoFill": "Tự động điền mật khẩu Sudo",
"sudoPasswordAutoFillDesc": "Lưu trữ mật khẩu sudo để các số liệu máy chủ, lời nhắc terminal và các thành phần khác có thể chạy lệnh đặc quyền tự động.",
"sudoPasswordAutoFillLabel": "Tự động điền mật khẩu sudo",
@@ -335,7 +334,8 @@
"history": "Lịch sử",
"localTerminal": "Ga địa phương",
"sshTools": "Công cụ SSH",
"terminal": "Terminal"
"terminal": "Terminal",
"macros": "Macro"
},
"sshTools": {
"broadcastInputPlaceholder": "Nhập vào đây để phát tán thao tác phím...",
@@ -363,5 +363,112 @@
"off": "Tắt",
"on": "TRÊN",
"title": "Cài đặt mặc định kết nối"
},
"keybindings": {
"copy": "Sao chép lựa chọn",
"paste": "Dán",
"sendControlCode": "Gửi tín hiệu Ctrl+chữ cái",
"sendText": "Gửi tin nhắn",
"controlCodeLabel": "Thư",
"textLabel": "Văn bản để gửi",
"appendEnterLabel": "Nhấn Enter sau khi gửi",
"clipboardPermissionNote": "Một số trình duyệt có thể yêu cầu quyền truy cập clipboard khi sử dụng lần đầu.",
"textRequired": "Nhập văn bản cần gửi",
"controlCodeRequired": "Nhập một chữ cái duy nhất cho mã điều khiển."
},
"macros": {
"title": "Macro",
"description": "Chạy một chuỗi lệnh đã lưu trong cửa sổ dòng lệnh và phản hồi lại những gì máy chủ gửi về.",
"target": "Mục tiêu",
"noTerminal": "Không có thiết bị đầu cuối nào đang mở",
"empty": "Chưa có macro nào.",
"emptyHint": "Tạo macro để tự động hóa quy trình trên thiết bị đầu cuối, chẳng hạn như đăng nhập, khởi động lại dịch vụ hoặc trả lời các lời nhắc.",
"create": "Macro mới",
"back": "Quay lại phần macro",
"name": "Tên",
"namePlaceholder": "Khởi động lại máy chủ web",
"nameRequired": "Đặt tên cho macro trước khi lưu.",
"descriptionLabel": "Sự miêu tả",
"descriptionPlaceholder": "Chức năng của macro này (tùy chọn)",
"unsaved": "Thay đổi chưa được lưu",
"save": "Cứu",
"saved": "Macro đã được lưu",
"saveFailed": "Không thể lưu macro",
"delete": "Xóa bỏ",
"deleteTitle": "Xóa macro này?",
"deleteBody": "\"{{name}}\" sẽ bị xóa vĩnh viễn. Không thể hoàn tác.",
"deleted": "Macro đã bị xóa",
"cancel": "Hủy bỏ",
"run": "Chạy",
"stop": "Dừng lại",
"running": "Đang chạy...",
"completed": "Macro đã hoàn thành",
"failed": "Macro bị lỗi",
"loadFailed": "Không thể tải macro",
"needTerminal": "Trước tiên hãy mở và kết nối thiết bị đầu cuối.",
"noSteps": "Macro này chưa có bước nào. Hãy thêm một bước bên dưới.",
"addStep": "Thêm bước",
"stepNumber": "Bước {{number}}",
"removeStep": "Xóa bước",
"confirmRunTitle": "Chạy macro này?",
"confirmRunBody": "\"{{name}}\" sẽ gửi lệnh đến {{host}}.",
"confirmRunAction": "Chạy macro",
"stepTypes": {
"send": {
"label": "Gửi lệnh",
"help": "Nhập văn bản vào cửa sổ dòng lệnh, giống như khi bạn tự nhập văn bản."
},
"wait": {
"label": "Chờ kết quả đầu ra",
"help": "Tạm dừng cho đến khi máy chủ in ra đoạn văn bản bạn đã đặt tên, ví dụ như lời nhắc nhập mật khẩu."
},
"delay": {
"label": "Tạm dừng",
"help": "Chờ một khoảng thời gian cố định trước khi thực hiện bước tiếp theo."
},
"if": {
"label": "Nếu đầu ra chứa",
"help": "Kiểm tra tất cả những gì máy chủ đã in ra cho đến nay, sau đó chạy một trong hai nhánh."
},
"repeat": {
"label": "Lặp lại",
"help": "Thực thi các bước bên trong nó một số lần nhất định."
}
},
"send": {
"text": "Lệnh hoặc văn bản",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "Nhấn Enter sau khi gửi",
"pressEnterHelp": "Tắt tùy chọn này để nhập văn bản mà không cần gửi đi."
},
"wait": {
"pattern": "Chờ cho đến khi đầu ra chứa",
"patternPlaceholder": "mật khẩu:",
"timeout": "Bỏ cuộc sau",
"onTimeout": "Nếu nó không bao giờ xuất hiện",
"stop": "Dừng macro",
"continue": "Cứ tiếp tục đi"
},
"delay": {
"duration": "Chờ đợi"
},
"ifStep": {
"pattern": "Nếu đầu ra chứa",
"patternPlaceholder": "Đang hoạt động: đang hoạt động (đang chạy)",
"then": "Sau đó hãy làm như thế này",
"else": "Nếu không thì hãy làm như thế này",
"emptyBranch": "Không có bước nào"
},
"repeat": {
"count": "Số lần",
"times": "lần"
},
"match": {
"useRegex": "So khớp với biểu thức chính quy",
"useRegexHelp": "Bỏ phần này đi để văn bản khớp chính xác với nội dung đã viết.",
"invalidRegex": "Đây không phải là biểu thức chính quy hợp lệ, nó sẽ được so khớp như văn bản thuần túy."
},
"seconds": "giây",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "已保存的主题",
"scrollbackBufferLabel": "回滚缓冲区",
"scrollbackMaxLines": "保留在历史记录中的最大行数",
"startupSnippetLabel": "启动代码片段",
"sudoPasswordAutoFill": "Sudo 密码自动填充",
"sudoPasswordAutoFillDesc": "存储 sudo 密码,以便主机指标、终端提示和其他操作可以自动运行特权命令。",
"sudoPasswordAutoFillLabel": "Sudo 密码自动填充",
@@ -335,7 +334,8 @@
"history": "历史记录",
"localTerminal": "本地终端",
"sshTools": "SSH 工具",
"terminal": "终端"
"terminal": "终端",
"macros": "宏"
},
"sshTools": {
"broadcastInputPlaceholder": "在此输入以广播按键...",
@@ -363,5 +363,112 @@
"off": "关闭",
"on": "开启",
"title": "连接默认值"
},
"keybindings": {
"copy": "复制选择",
"paste": "粘贴",
"sendControlCode": "发送 Ctrl+字母信号",
"sendText": "发送文本",
"controlCodeLabel": "字母",
"textLabel": "发送的文本",
"appendEnterLabel": "发送后按回车键",
"clipboardPermissionNote": "某些浏览器在首次使用时可能会提示授予剪贴板权限。",
"textRequired": "请输入要发送的文本",
"controlCodeRequired": "请输入单个字母作为控制码"
},
"macros": {
"title": "宏",
"description": "在终端中运行已保存的一系列命令,并对服务器返回的内容做出反应。",
"target": "目标",
"noTerminal": "没有终端打开",
"empty": "暂无宏",
"emptyHint": "创建一个宏来自动化终端流程,例如登录、重启服务或回答提示。",
"create": "新宏",
"back": "返回宏",
"name": "名称",
"namePlaceholder": "重启 Web 服务器",
"nameRequired": "保存之前,请给宏命名。",
"descriptionLabel": "描述",
"descriptionPlaceholder": "此宏的功能(可选)",
"unsaved": "未保存的更改",
"save": "保存",
"saved": "宏已保存",
"saveFailed": "无法保存宏",
"delete": "删除",
"deleteTitle": "删除这个宏?",
"deleteBody": "“{{name}}”将被永久删除。此操作无法撤销。",
"deleted": "宏已删除",
"cancel": "取消",
"run": "运行",
"stop": "停止",
"running": "运行中...",
"completed": "宏已完成",
"failed": "宏失败",
"loadFailed": "无法加载宏。",
"needTerminal": "首先打开并连接终端",
"noSteps": "此宏目前尚无步骤。请在下方添加步骤。",
"addStep": "添加步骤",
"stepNumber": "步骤 {{number}}",
"removeStep": "移除步骤",
"confirmRunTitle": "运行这个宏?",
"confirmRunBody": "\"{{name}}\" 将向 {{host}} 发送命令。",
"confirmRunAction": "运行宏",
"stepTypes": {
"send": {
"label": "发送命令",
"help": "在终端中输入文本,就像你手动输入一样。"
},
"wait": {
"label": "等待输出",
"help": "暂停,直到服务器打印出您指定的文本,例如密码提示。"
},
"delay": {
"label": "暂停",
"help": "等待一段固定时间后再执行下一步操作。"
},
"if": {
"label": "如果输出包含",
"help": "检查服务器目前为止打印的所有内容,然后运行两个分支中的一个。"
},
"repeat": {
"label": "重复",
"help": "将其中的步骤执行设定的次数。"
}
},
"send": {
"text": "命令或文本",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "发送后按回车键",
"pressEnterHelp": "关闭此功能即可输入文本而不提交。"
},
"wait": {
"pattern": "等待输出包含",
"patternPlaceholder": "密码:",
"timeout": "放弃",
"onTimeout": "如果它从未出现",
"stop": "停止宏",
"continue": "无论如何都继续"
},
"delay": {
"duration": "等待"
},
"ifStep": {
"pattern": "如果输出包含",
"patternPlaceholder": "活跃:正在运行",
"then": "然后这样做",
"else": "否则就这样做",
"emptyBranch": "没有步骤"
},
"repeat": {
"count": "次数",
"times": "次"
},
"match": {
"useRegex": "使用正则表达式进行匹配",
"useRegexHelp": "关闭此选项以完全按原文匹配文本。",
"invalidRegex": "这不是有效的正则表达式,它将被匹配为纯文本。"
},
"seconds": "秒",
"secondsShort": "{{count}}s"
}
}
@@ -307,7 +307,6 @@
"savedThemesLabel": "已儲存的主題",
"scrollbackBufferLabel": "回滾緩衝區",
"scrollbackMaxLines": "歷史記錄保留的最大行數",
"startupSnippetLabel": "啟動程式碼片段",
"sudoPasswordAutoFill": "Sudo 密碼自動填寫",
"sudoPasswordAutoFillDesc": "儲存 sudo 密碼,以便主機指標、終端機提示及其他功能能自動執行特權命令。",
"sudoPasswordAutoFillLabel": "Sudo 密碼自動填入",
@@ -335,7 +334,8 @@
"history": "歷史記錄",
"localTerminal": "本地端",
"sshTools": "SSH 工具",
"terminal": "終端機"
"terminal": "終端機",
"macros": "宏"
},
"sshTools": {
"broadcastInputPlaceholder": "在此輸入以廣播按鍵輸入...",
@@ -363,5 +363,112 @@
"off": "離開",
"on": "在",
"title": "連接預設值"
},
"keybindings": {
"copy": "複製選擇",
"paste": "貼上",
"sendControlCode": "發送 Ctrl+字母訊號",
"sendText": "傳簡訊",
"controlCodeLabel": "信",
"textLabel": "發送的文字",
"appendEnterLabel": "發送後按回車鍵",
"clipboardPermissionNote": "某些瀏覽器在首次使用時可能會提示授予剪貼簿權限。",
"textRequired": "請輸入要發送的文字",
"controlCodeRequired": "請輸入一個字母作為控制碼"
},
"macros": {
"title": "宏",
"description": "在終端機中執行已儲存的一系列命令,並對伺服器傳回的內容做出反應。",
"target": "目標",
"noTerminal": "沒有終端機打開",
"empty": "暫無宏",
"emptyHint": "建立一個巨集來自動化終端流程,例如登入、重新啟動服務或回答提示。",
"create": "新宏",
"back": "回傳宏",
"name": "姓名",
"namePlaceholder": "重啟Web伺服器",
"nameRequired": "儲存之前,請先為巨集命名。",
"descriptionLabel": "描述",
"descriptionPlaceholder": "此巨集的功能(選用)",
"unsaved": "未儲存的更改",
"save": "節省",
"saved": "巨集已儲存",
"saveFailed": "無法保存巨集",
"delete": "刪除",
"deleteTitle": "刪除這個巨集?",
"deleteBody": "“{{name}}”將永久刪除。此操作無法撤銷。",
"deleted": "巨集已刪除",
"cancel": "取消",
"run": "跑步",
"stop": "停止",
"running": "跑步...",
"completed": "巨集已完成",
"failed": "巨集失敗",
"loadFailed": "無法載入巨集。",
"needTerminal": "首先打開並連接終端。",
"noSteps": "此巨集目前尚無步驟。請在下方新增步驟。",
"addStep": "新增步驟",
"stepNumber": "步驟 {{number}}",
"removeStep": "移除步驟",
"confirmRunTitle": "運行這個巨集?",
"confirmRunBody": "\"{{name}}\" 將向 {{host}} 傳送指令。",
"confirmRunAction": "運行巨集",
"stepTypes": {
"send": {
"label": "發送命令",
"help": "在終端機中輸入文本,就像你手動輸入一樣。"
},
"wait": {
"label": "等待輸出",
"help": "暫停,直到伺服器列印您指定的文本,例如密碼提示。"
},
"delay": {
"label": "暫停",
"help": "等待一段固定時間後再執行下一步操作。"
},
"if": {
"label": "如果輸出包含",
"help": "檢查伺服器目前為止列印的所有內容,然後執行兩個分支中的一個。"
},
"repeat": {
"label": "重複",
"help": "將其中的步驟執行設定的次數。"
}
},
"send": {
"text": "命令或文字",
"textPlaceholder": "systemctl restart nginx",
"pressEnter": "發送後按回車鍵",
"pressEnterHelp": "關閉此功能即可輸入文字而不提交。"
},
"wait": {
"pattern": "等待輸出包含",
"patternPlaceholder": "密碼:",
"timeout": "放棄",
"onTimeout": "如果它從未出現",
"stop": "停止宏",
"continue": "無論如何,繼續前進。"
},
"delay": {
"duration": "等待"
},
"ifStep": {
"pattern": "如果輸出包含",
"patternPlaceholder": "活躍:正在運行",
"then": "然後這樣做",
"else": "否則就這樣做",
"emptyBranch": "沒有步驟"
},
"repeat": {
"count": "次數",
"times": "時代"
},
"match": {
"useRegex": "使用正規表示式進行匹配",
"useRegexHelp": "請刪除此行以完全按照原文填寫。",
"invalidRegex": "這不是有效的正規表示式,它將被匹配為純文字。"
},
"seconds": "秒",
"secondsShort": "{{count}}s"
}
}
+44 -1
View File
@@ -79,6 +79,11 @@
"id": "ssh-tools",
"titleKey": "nav.sshTools",
"icon": "Hammer"
},
{
"id": "macros",
"titleKey": "nav.macros",
"icon": "Braces"
}
],
"actionSlots": [
@@ -752,9 +757,47 @@
"labelKey": "hosts.linkClickBehaviorDirect"
}
]
},
{
"key": "macros",
"type": "json",
"labelKey": "macros.title",
"hidden": true,
"default": []
}
]
}
},
"keybindingActions": [
{
"id": "copy"
},
{
"id": "paste"
},
{
"id": "sendControlCode",
"params": {
"controlCode": {
"type": "string",
"required": true,
"pattern": "^[a-zA-Z]$"
}
}
},
{
"id": "sendText",
"params": {
"text": {
"type": "string",
"maxLength": 65536,
"required": true
},
"appendEnter": {
"type": "boolean"
}
}
}
]
},
"locales": "locales",
"platforms": ["linux", "win32", "darwin"]
+5 -1
View File
@@ -22,7 +22,10 @@ import {
import { ADMIN_KEYS } from "./settings.js";
import { commandHistory } from "./tables.js";
import { createTerminalSocket } from "./terminal-socket.js";
import { validateAdminSettings } from "./settings-validation.js";
import {
validateAdminSettings,
validateUserSettings,
} from "./settings-validation.js";
function toInfo(session: TerminalSession): LiveSessionInfo {
return {
@@ -57,6 +60,7 @@ function optionalService<T extends object>(
export async function activate(ctx: PluginContext) {
const log = createTerminalLogger(ctx.log);
ctx.settings.onValidate("admin", validateAdminSettings);
ctx.settings.onValidate("user", validateUserSettings);
const table = await ctx.db.define(commandHistory);
const history = createHistoryRepository(ctx.db, table);
@@ -30,3 +30,38 @@ export function validateAdminSettings(
}
return errors;
}
const MAX_MACROS = 100;
const MAX_MACROS_BYTES = 512 * 1024;
/** Save-time checks for the user settings: macros stay bounded. */
export function validateUserSettings(
values: Record<string, unknown>,
): Record<string, string> {
const errors: Record<string, string> = {};
if (values.macros === undefined || values.macros === null) return errors;
let macros = values.macros;
if (typeof macros === "string") {
try {
macros = JSON.parse(macros);
} catch {
macros = null;
}
}
const valid =
Array.isArray(macros) &&
macros.length <= MAX_MACROS &&
JSON.stringify(macros).length <= MAX_MACROS_BYTES &&
macros.every(
(macro) =>
!!macro &&
typeof macro === "object" &&
typeof (macro as { id?: unknown }).id === "string" &&
typeof (macro as { name?: unknown }).name === "string" &&
Array.isArray((macro as { steps?: unknown }).steps),
);
if (!valid) {
errors.macros = `Macros must be a list of at most ${MAX_MACROS} valid macros`;
}
return errors;
}
@@ -1,5 +1,6 @@
import { lazy, Suspense, type ComponentType } from "react";
import {
Braces,
Hammer,
History,
Laptop,
@@ -33,6 +34,17 @@ import { HostTerminalSection } from "./settings/HostTerminalSection";
import { resetTouchInputSettingsCache } from "./terminal/touch-input-settings-store";
import { hostSetting } from "./terminal-api";
import { SshToolsPanel } from "./ssh-tools/SshToolsPanel";
import { MacrosPanel } from "./macros/MacrosPanel";
import {
TERMINAL_KEYBINDING_DEFAULTS,
validateSendControlCode,
validateSendText,
} from "./lib/keybinding-dispatch";
import {
PasteNote,
SendControlCodeEditor,
SendTextEditor,
} from "./lib/keybinding-editors";
import { TerminalDefaultsSettings } from "./settings/TerminalDefaultsSettings";
import {
NewHostFontSetting,
@@ -191,6 +203,45 @@ export function activate(app: TermixApp): void {
separatorAfter: true,
});
app.registerPanel(
"macros",
MacrosPanel as unknown as ComponentType<PanelProps>,
);
app.registerRailItem({
id: "macros",
icon: Braces,
titleKey: "nav.macros",
after: "ssh-tools",
hideable: true,
promotable: true,
rightDockable: true,
separatorAfter: true,
});
// The keys a user can bind to the terminal's own actions, and its
// built-in keys they can rebind. The terminal runs these itself.
app.registerKeybindingAction({ id: "copy", titleKey: "keybindings.copy" });
app.registerKeybindingAction({
id: "paste",
titleKey: "keybindings.paste",
editor: PasteNote,
});
app.registerKeybindingAction({
id: "sendControlCode",
titleKey: "keybindings.sendControlCode",
editor: SendControlCodeEditor,
validate: validateSendControlCode,
});
app.registerKeybindingAction({
id: "sendText",
titleKey: "keybindings.sendText",
editor: SendTextEditor,
validate: validateSendText,
});
for (const binding of TERMINAL_KEYBINDING_DEFAULTS) {
app.registerKeybindingDefault(binding);
}
// Places other plugins can fill: toolbar buttons and readouts, a side
// panel and overlays that follow the session's connection flow.
app.declareActionSlot({ id: TERMINAL_TOOLBAR_SLOT, accepts: ["button"] });
@@ -1,27 +1,26 @@
import type { Terminal } from "@xterm/xterm";
import { type KeybindingAction } from "@termix/plugin-sdk/ui";
import { invokeAction } from "@termix/plugin-sdk/frontend";
export interface SnippetHostContext {
ip?: string;
username?: string;
port?: number | string;
name?: string;
}
import type {
KeybindingAction,
KeybindingDefaultContribution,
} from "@termix/plugin-sdk/frontend";
export interface KeybindingDispatchContext {
terminal: Terminal;
webSocketRef: React.MutableRefObject<WebSocket | null>;
writeTextToClipboard: (text: string) => Promise<boolean>;
readTextFromClipboard: () => Promise<string>;
/** Host context used to resolve $HOST/$USER/$PORT/$NAME in runSnippet actions. */
hostContext?: SnippetHostContext | null;
/**
* Called instead of sending the snippet directly when its content still has
* unresolved $INPUT_n placeholders after host-variable substitution -- the
* caller is expected to collect values (e.g. via a dialog) and send itself.
*/
onSnippetNeedsInputs?: (snippet: { id: string; content: string }) => void;
}
/** The keybinding actions the terminal runs itself. */
export const TERMINAL_KEYBINDING_ACTIONS = [
"copy",
"paste",
"sendControlCode",
"sendText",
] as const;
export function isTerminalKeybindingAction(type: string): boolean {
return (TERMINAL_KEYBINDING_ACTIONS as readonly string[]).includes(type);
}
export function sendRawToSocket(
@@ -33,10 +32,19 @@ export function sendRawToSocket(
}
}
function text(action: KeybindingAction, key: string): string {
const value = action[key];
return typeof value === "string" ? value : "";
}
/**
* Runs one of the terminal's own bound actions. False for any other type,
* which the terminal hands on to whoever registered it.
*/
export function dispatchKeybindingAction(
action: KeybindingAction,
ctx: KeybindingDispatchContext,
): void {
): boolean {
const sendRaw = (data: string) => sendRawToSocket(ctx.webSocketRef, data);
switch (action.type) {
@@ -46,58 +54,96 @@ export function dispatchKeybindingAction(
ctx.writeTextToClipboard(selection);
ctx.terminal.clearSelection();
}
return;
return true;
}
case "paste": {
ctx.readTextFromClipboard().then((text) => {
if (text) ctx.terminal.paste(text);
ctx.readTextFromClipboard().then((value) => {
if (value) ctx.terminal.paste(value);
});
return;
return true;
}
case "sendControlCode": {
if (!action.controlCode) return;
const code = action.controlCode.toLowerCase().charCodeAt(0) - 96;
const letter = text(action, "controlCode");
if (!letter) return true;
const code = letter.toLowerCase().charCodeAt(0) - 96;
if (code >= 1 && code <= 26) sendRaw(String.fromCharCode(code));
return;
return true;
}
case "sendText": {
sendRaw((action.text ?? "") + (action.appendEnter ? "\r" : ""));
return;
}
case "runSnippet": {
if (!action.snippetId) return;
const snippetId = Number(action.snippetId);
if (!Number.isFinite(snippetId)) return;
// Resolving with no inputValues first tells us whether the snippet
// still needs them; the plugin decides and reports back.
void invokeAction(
"snippets.resolveForTerminal",
snippetId,
ctx.hostContext ?? null,
).then((result) => {
const resolved = result as
{ needsInputs: boolean; content: string } | null | undefined;
if (!resolved) return;
if (resolved.needsInputs) {
ctx.onSnippetNeedsInputs?.({
id: action.snippetId!,
content: resolved.content,
});
return;
}
sendRaw(resolved.content + (action.appendEnter !== false ? "\r" : ""));
});
return;
}
case "nextTab":
case "previousTab":
case "openCommandPalette": {
window.dispatchEvent(
new CustomEvent("termix:global-keybinding", {
detail: { type: action.type },
}),
);
return;
sendRaw(text(action, "text") + (action.appendEnter === true ? "\r" : ""));
return true;
}
default:
return false;
}
}
export function validateSendText(action: KeybindingAction): string | null {
return text(action, "text").trim() ? null : "keybindings.textRequired";
}
export function validateSendControlCode(
action: KeybindingAction,
): string | null {
return /^[a-zA-Z]$/.test(text(action, "controlCode"))
? null
: "keybindings.controlCodeRequired";
}
function combo(
key: string,
modifiers: { ctrl?: boolean; alt?: boolean; shift?: boolean; meta?: boolean },
) {
return {
key,
isCode: false,
ctrl: !!modifiers.ctrl,
alt: !!modifiers.alt,
shift: !!modifiers.shift,
meta: !!modifiers.meta,
};
}
/** The terminal's built-in keys, listed so a user can rebind them. */
export const TERMINAL_KEYBINDING_DEFAULTS: KeybindingDefaultContribution[] = [
{
id: "default-copy-ctrlc",
combo: combo("c", { ctrl: true }),
descriptionKey: "keybindings.builtIn.copyCtrlC",
},
{
id: "default-copy-ctrlshiftc",
combo: combo("c", { ctrl: true, shift: true }),
descriptionKey: "keybindings.builtIn.copyCtrlShiftC",
},
{
id: "default-copy-cmdc",
combo: combo("c", { meta: true }),
descriptionKey: "keybindings.builtIn.copyCmdC",
},
{
id: "default-paste-ctrlshiftv",
combo: combo("v", { ctrl: true, shift: true }),
descriptionKey: "keybindings.builtIn.pasteCtrlShiftV",
},
{
id: "default-ctrlaltw",
combo: combo("w", { ctrl: true, alt: true }),
descriptionKey: "keybindings.builtIn.ctrlW",
},
{
id: "default-ctrlaltt",
combo: combo("t", { ctrl: true, alt: true }),
descriptionKey: "keybindings.builtIn.ctrlT",
},
{
id: "default-ctrlaltn",
combo: combo("n", { ctrl: true, alt: true }),
descriptionKey: "keybindings.builtIn.ctrlN",
},
{
id: "default-ctrlaltq",
combo: combo("q", { ctrl: true, alt: true }),
descriptionKey: "keybindings.builtIn.ctrlQ",
},
];
@@ -0,0 +1,81 @@
import {
useTranslation,
type KeybindingActionEditorProps,
} from "@termix/plugin-sdk/frontend";
import { Input } from "@termix/plugin-sdk/ui";
export function PasteNote() {
const { t } = useTranslation();
return (
<span className="text-xs text-muted-foreground">
{t("keybindings.clipboardPermissionNote")}
</span>
);
}
export function SendControlCodeEditor({
action,
onChange,
}: KeybindingActionEditorProps) {
const { t } = useTranslation();
const value =
typeof action.controlCode === "string" ? action.controlCode : "";
return (
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold">
{t("keybindings.controlCodeLabel")}
</label>
<Input
aria-label={t("keybindings.controlCodeLabel")}
value={value}
maxLength={1}
onChange={(e) =>
onChange({
type: action.type,
controlCode: e.target.value.toLowerCase() || undefined,
})
}
placeholder="w"
className="w-16 font-mono"
/>
</div>
);
}
export function SendTextEditor({
action,
onChange,
}: KeybindingActionEditorProps) {
const { t } = useTranslation();
const text = typeof action.text === "string" ? action.text : "";
const appendEnter = action.appendEnter === true;
return (
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold">
{t("keybindings.textLabel")}
</label>
<textarea
aria-label={t("keybindings.textLabel")}
value={text}
onChange={(e) =>
onChange({ type: action.type, text: e.target.value, appendEnter })
}
className="w-full h-24 px-3 py-2 text-xs bg-background border border-border text-foreground placeholder:text-muted-foreground resize-none outline-none focus:ring-1 focus:ring-ring font-mono"
/>
<label className="flex items-center gap-2 text-xs text-muted-foreground">
<input
type="checkbox"
checked={appendEnter}
onChange={(e) =>
onChange({
type: action.type,
text,
appendEnter: e.target.checked,
})
}
/>
{t("keybindings.appendEnterLabel")}
</label>
</div>
);
}
@@ -0,0 +1,864 @@
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
useSyncExternalStore,
} from "react";
import {
useSettings,
useTranslation,
type PanelProps,
} from "@termix/plugin-sdk/frontend";
import {
ArrowLeft,
Braces,
ChevronDown,
Clock,
CornerDownRight,
GitBranch,
Plus,
Repeat,
Save,
Square,
Terminal as TerminalIcon,
Trash2,
Play,
} from "lucide-react";
import { toast } from "sonner";
import {
Button,
Input,
Textarea,
Checkbox,
EmptyState,
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@termix/plugin-sdk/ui";
import {
mergeLocalMacros,
sanitizeTerminalMacros,
runTerminalMacro,
type MacroStep,
type TerminalMacro,
} from "./terminal-macros";
import {
getSessionHandle,
sessionsSnapshot,
subscribeSessions,
} from "../session-registry";
function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : "";
}
const STEP_TYPES = ["send", "wait", "delay", "if", "repeat"] as const;
const STEP_ICONS = {
send: TerminalIcon,
wait: CornerDownRight,
delay: Clock,
if: GitBranch,
repeat: Repeat,
} as const;
function id(): string {
return typeof crypto.randomUUID === "function"
? crypto.randomUUID()
: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
}
function createStep(type: MacroStep["type"]): MacroStep {
const stepId = id();
switch (type) {
case "send":
return { id: stepId, type, text: "", pressEnter: true };
case "delay":
return { id: stepId, type, milliseconds: 1000 };
case "wait":
return {
id: stepId,
type,
pattern: "",
isRegex: false,
timeoutMs: 10_000,
onTimeout: "stop",
};
case "if":
return {
id: stepId,
type,
pattern: "",
isRegex: false,
then: [],
else: [],
};
case "repeat":
return { id: stepId, type, count: 2, steps: [] };
}
}
function isValidRegex(pattern: string): boolean {
try {
new RegExp(pattern);
return true;
} catch {
return false;
}
}
/** Milliseconds are an implementation detail, so the editor works in seconds. */
function SecondsInput({
valueMs,
onChangeMs,
minMs,
label,
}: {
valueMs: number;
onChangeMs: (ms: number) => void;
minMs: number;
label: string;
}) {
const { t } = useTranslation();
return (
<label className="flex items-center gap-2 text-xs">
<span className="text-muted-foreground">{label}</span>
<Input
className="h-7 w-20 rounded-none text-xs"
type="number"
min={minMs / 1000}
max={300}
step={0.5}
value={valueMs / 1000}
onChange={(event) => {
const seconds = Number(event.target.value);
onChangeMs(
Number.isFinite(seconds)
? Math.max(minMs, Math.min(300_000, Math.round(seconds * 1000)))
: minMs,
);
}}
/>
<span className="text-muted-foreground">{t("macros.seconds")}</span>
</label>
);
}
/** Shared text/regex matcher field used by both "wait" and "if". */
function MatchField({
pattern,
isRegex,
label,
placeholder,
onChange,
}: {
pattern: string;
isRegex: boolean;
label: string;
placeholder: string;
onChange: (next: { pattern?: string; isRegex?: boolean }) => void;
}) {
const { t } = useTranslation();
const badRegex = isRegex && pattern.length > 0 && !isValidRegex(pattern);
return (
<div className="space-y-1.5">
<div className="text-xs text-muted-foreground">{label}</div>
<Input
className={`h-8 rounded-none font-mono text-xs ${
badRegex ? "border-destructive" : ""
}`}
placeholder={placeholder}
value={pattern}
onChange={(event) => onChange({ pattern: event.target.value })}
/>
{badRegex && (
<div className="text-[11px] text-destructive">
{t("macros.match.invalidRegex")}
</div>
)}
<label className="flex items-center gap-2 text-[11px] text-muted-foreground">
<Checkbox
checked={isRegex}
onCheckedChange={(checked) => onChange({ isRegex: checked === true })}
/>
{t("macros.match.useRegex")}
</label>
</div>
);
}
function AddStepMenu({
onAdd,
compact,
}: {
onAdd: (type: MacroStep["type"]) => void;
compact?: boolean;
}) {
const { t } = useTranslation();
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="sm"
variant="outline"
className={`rounded-none ${compact ? "h-7 text-[11px]" : "h-8 w-full text-xs"}`}
>
<Plus className="mr-1 size-3" />
{t("macros.addStep")}
<ChevronDown className="ml-1 size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-72 rounded-none">
{STEP_TYPES.map((type) => {
const Icon = STEP_ICONS[type];
return (
<DropdownMenuItem
key={type}
className="flex-col items-start gap-0.5 rounded-none"
onSelect={() => onAdd(type)}
>
<span className="flex items-center gap-2 text-xs font-medium">
<Icon className="size-3.5" />
{t(`macros.stepTypes.${type}.label`)}
</span>
<span className="pl-5 text-[11px] leading-snug text-muted-foreground">
{t(`macros.stepTypes.${type}.help`)}
</span>
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}
function StepList({
steps,
onChange,
depth = 0,
}: {
steps: MacroStep[];
onChange: (steps: MacroStep[]) => void;
depth?: number;
}) {
const { t } = useTranslation();
const update = (index: number, step: MacroStep) => {
const next = [...steps];
next[index] = step;
onChange(next);
};
return (
<div className="flex flex-col gap-2">
{steps.map((step, index) => {
const Icon = STEP_ICONS[step.type];
return (
<div key={step.id} className="border border-border bg-muted/20">
<div className="flex items-center gap-2 border-b border-border/60 px-2 py-1.5">
<Icon className="size-3.5 shrink-0 text-muted-foreground" />
<span className="text-xs font-medium">
{t(`macros.stepTypes.${step.type}.label`)}
</span>
<Button
size="icon"
variant="ghost"
className="ml-auto size-6 rounded-none"
aria-label={t("macros.removeStep")}
onClick={() => onChange(steps.filter((_, i) => i !== index))}
>
<Trash2 className="size-3" />
</Button>
</div>
<div className="space-y-2 p-2">
{step.type === "send" && (
<div className="space-y-2">
<Textarea
className="min-h-16 rounded-none font-mono text-xs"
placeholder={t("macros.send.textPlaceholder")}
value={step.text}
onChange={(event) =>
update(index, { ...step, text: event.target.value })
}
/>
<label className="flex items-center gap-2 text-[11px] text-muted-foreground">
<Checkbox
checked={step.pressEnter}
onCheckedChange={(checked) =>
update(index, { ...step, pressEnter: checked === true })
}
/>
{t("macros.send.pressEnter")}
</label>
</div>
)}
{step.type === "delay" && (
<SecondsInput
label={t("macros.delay.duration")}
valueMs={step.milliseconds}
minMs={0}
onChangeMs={(milliseconds) =>
update(index, { ...step, milliseconds })
}
/>
)}
{step.type === "wait" && (
<div className="space-y-2">
<MatchField
pattern={step.pattern}
isRegex={step.isRegex !== false}
label={t("macros.wait.pattern")}
placeholder={t("macros.wait.patternPlaceholder")}
onChange={(next) => update(index, { ...step, ...next })}
/>
<SecondsInput
label={t("macros.wait.timeout")}
valueMs={step.timeoutMs}
minMs={100}
onChangeMs={(timeoutMs) =>
update(index, { ...step, timeoutMs })
}
/>
<div className="space-y-1">
<div className="text-xs text-muted-foreground">
{t("macros.wait.onTimeout")}
</div>
<div className="flex gap-1">
{(["stop", "continue"] as const).map((mode) => (
<Button
key={mode}
size="sm"
variant={
step.onTimeout === mode ? "default" : "outline"
}
className="h-7 flex-1 rounded-none text-[11px]"
onClick={() =>
update(index, { ...step, onTimeout: mode })
}
>
{t(`macros.wait.${mode}`)}
</Button>
))}
</div>
</div>
</div>
)}
{step.type === "if" && (
<div className="space-y-3">
<MatchField
pattern={step.pattern}
isRegex={step.isRegex !== false}
label={t("macros.ifStep.pattern")}
placeholder={t("macros.ifStep.patternPlaceholder")}
onChange={(next) => update(index, { ...step, ...next })}
/>
<div className="border-l-2 border-emerald-500/40 pl-2">
<div className="mb-1 text-[11px] font-medium text-emerald-500">
{t("macros.ifStep.then")}
</div>
<StepList
steps={step.then}
depth={depth + 1}
onChange={(then) => update(index, { ...step, then })}
/>
</div>
<div className="border-l-2 border-amber-500/40 pl-2">
<div className="mb-1 text-[11px] font-medium text-amber-500">
{t("macros.ifStep.else")}
</div>
<StepList
steps={step.else}
depth={depth + 1}
onChange={(elseSteps) =>
update(index, { ...step, else: elseSteps })
}
/>
</div>
</div>
)}
{step.type === "repeat" && (
<div className="space-y-2">
<label className="flex items-center gap-2 text-xs">
<span className="text-muted-foreground">
{t("macros.repeat.count")}
</span>
<Input
className="h-7 w-20 rounded-none text-xs"
type="number"
min={1}
max={100}
value={step.count}
onChange={(event) =>
update(index, {
...step,
count: Math.max(
1,
Math.min(100, Number(event.target.value) || 1),
),
})
}
/>
<span className="text-muted-foreground">
{t("macros.repeat.times")}
</span>
</label>
<div className="border-l-2 border-border pl-2">
<StepList
steps={step.steps}
depth={depth + 1}
onChange={(nested) =>
update(index, { ...step, steps: nested })
}
/>
</div>
</div>
)}
</div>
</div>
);
})}
{depth < 4 && (
<AddStepMenu
compact={depth > 0}
onAdd={(type) => onChange([...steps, createStep(type)])}
/>
)}
</div>
);
}
/**
* Macros: a saved series of steps typed into the terminal the user is
* working in, waiting for and branching on what the server sends back.
* They are the user's own setting, so they follow them to every browser.
*/
export function MacrosPanel({ targetTab }: PanelProps) {
const { t } = useTranslation();
const settings = useSettings("user");
const [draft, setDraft] = useState<TerminalMacro | null>(null);
const [dirty, setDirty] = useState(false);
const [runningId, setRunningId] = useState<string | null>(null);
const [pendingDelete, setPendingDelete] = useState<TerminalMacro | null>(
null,
);
const [pendingRun, setPendingRun] = useState<TerminalMacro | null>(null);
const abortRef = useRef<AbortController | null>(null);
const confirmedRef = useRef<Set<string>>(new Set());
const movedLocalRef = useRef(false);
const sessions = useSyncExternalStore(subscribeSessions, sessionsSnapshot);
const macros = useMemo(
() => sanitizeTerminalMacros(settings.values.macros),
[settings.values.macros],
);
const { save: saveSettings } = settings;
useEffect(() => () => abortRef.current?.abort(), []);
// Macros a browser kept before 2.9.0 move into the setting once.
useEffect(() => {
if (!settings.loaded || movedLocalRef.current) return;
movedLocalRef.current = true;
const merged = mergeLocalMacros(macros);
if (!merged) return;
saveSettings({ macros: merged.macros })
.then(merged.clear)
.catch(() => toast.error(t("macros.saveFailed")));
}, [settings.loaded, macros, saveSettings, t]);
const target = useMemo(
() =>
sessions.find((session) => session.id === targetTab?.id) ?? sessions[0],
[sessions, targetTab?.id],
);
const updateDraft = useCallback((next: TerminalMacro) => {
setDraft(next);
setDirty(true);
}, []);
const createMacro = () => {
const now = new Date().toISOString();
setDraft({
id: id(),
name: "",
description: "",
steps: [],
createdAt: now,
updatedAt: now,
});
setDirty(true);
};
const persist = async (next: TerminalMacro[]) => {
await saveSettings({ macros: next });
};
const save = async () => {
if (!draft) return;
if (!draft.name.trim()) {
toast.error(t("macros.nameRequired"));
return;
}
const saved: TerminalMacro = {
...draft,
name: draft.name.trim(),
updatedAt: new Date().toISOString(),
};
try {
await persist(
macros.some((macro) => macro.id === saved.id)
? macros.map((macro) => (macro.id === saved.id ? saved : macro))
: [...macros, saved],
);
setDraft(saved);
setDirty(false);
toast.success(t("macros.saved"));
} catch (error) {
toast.error(getErrorMessage(error) || t("macros.saveFailed"));
}
};
const confirmDelete = async () => {
if (!pendingDelete) return;
const next = macros.filter((macro) => macro.id !== pendingDelete.id);
try {
await persist(next);
if (draft?.id === pendingDelete.id) {
setDraft(null);
setDirty(false);
}
toast.success(t("macros.deleted"));
} catch (error) {
toast.error(getErrorMessage(error) || t("macros.saveFailed"));
} finally {
setPendingDelete(null);
}
};
const execute = useCallback(
async (macro: TerminalMacro) => {
const terminal = target ? getSessionHandle(target.id) : null;
if (!terminal?.sendInput || !terminal.subscribeOutput) {
toast.error(t("macros.needTerminal"));
return;
}
const controller = new AbortController();
abortRef.current = controller;
setRunningId(macro.id);
try {
await runTerminalMacro(
macro,
{ send: terminal.sendInput, subscribe: terminal.subscribeOutput },
{ signal: controller.signal },
);
toast.success(t("macros.completed"));
} catch (error) {
if (!(error instanceof DOMException && error.name === "AbortError")) {
toast.error(getErrorMessage(error) || t("macros.failed"));
}
} finally {
abortRef.current = null;
setRunningId(null);
}
},
[target, t],
);
const requestRun = (macro: TerminalMacro) => {
if (!target || !getSessionHandle(target.id)?.sendInput) {
toast.error(t("macros.needTerminal"));
return;
}
if (confirmedRef.current.has(macro.id)) {
void execute(macro);
return;
}
setPendingRun(macro);
};
const targetLabel = target?.label ?? t("macros.noTerminal");
return (
<div className="flex h-full min-h-0 flex-col">
<div className="flex items-center gap-2 border-b border-border p-3">
{draft ? (
<Button
size="icon"
variant="ghost"
className="size-7 rounded-none"
aria-label={t("macros.back")}
onClick={() => {
setDraft(null);
setDirty(false);
}}
>
<ArrowLeft className="size-4" />
</Button>
) : (
<Braces className="size-4 shrink-0" />
)}
<div className="min-w-0 flex-1">
<div className="text-sm font-semibold">{t("macros.title")}</div>
<div className="truncate text-[11px] text-muted-foreground">
{t("macros.target")}: {targetLabel}
</div>
{!draft && (
<a
href="https://docs.termix.site/features/terminal/macros"
target="_blank"
rel="noreferrer"
className="text-[10px] text-accent-brand hover:underline"
>
{t("hosts.docsLink")}
</a>
)}
</div>
{!draft && (
<Button
size="icon"
variant="outline"
className="size-7 rounded-none"
aria-label={t("macros.create")}
onClick={createMacro}
>
<Plus className="size-3.5" />
</Button>
)}
</div>
<div className="min-h-0 flex-1 overflow-y-auto p-3">
{!draft ? (
macros.length === 0 ? (
<EmptyState
icon={Braces}
title={t("macros.empty")}
hint={t("macros.emptyHint")}
action={
<Button
size="sm"
variant="outline"
className="mt-1 rounded-none"
onClick={createMacro}
>
<Plus className="mr-1 size-3" />
{t("macros.create")}
</Button>
}
/>
) : (
<div className="flex flex-col gap-1">
{macros.map((macro) => {
const isRunning = runningId === macro.id;
return (
<div
key={macro.id}
className="group flex items-center gap-2 border border-border bg-muted/20 px-2 py-1.5 hover:bg-muted/40"
>
<button
type="button"
className="min-w-0 flex-1 text-left"
onClick={() => {
setDraft(structuredClone(macro));
setDirty(false);
}}
>
<div className="truncate text-xs font-medium">
{macro.name}
</div>
{macro.description && (
<div className="truncate text-[11px] text-muted-foreground">
{macro.description}
</div>
)}
</button>
<Button
size="icon"
variant="ghost"
className="size-6 shrink-0 rounded-none"
aria-label={
isRunning ? t("macros.stop") : t("macros.run")
}
onClick={() =>
isRunning
? abortRef.current?.abort()
: requestRun(macro)
}
>
{isRunning ? (
<Square className="size-3" />
) : (
<Play className="size-3" />
)}
</Button>
</div>
);
})}
</div>
)
) : (
<div className="space-y-3">
<div className="space-y-1.5">
<div className="text-xs text-muted-foreground">
{t("macros.name")}
</div>
<Input
className="h-8 rounded-none text-xs"
value={draft.name}
placeholder={t("macros.namePlaceholder")}
onChange={(event) =>
updateDraft({ ...draft, name: event.target.value })
}
/>
</div>
<div className="space-y-1.5">
<div className="text-xs text-muted-foreground">
{t("macros.descriptionLabel")}
</div>
<Textarea
className="min-h-14 rounded-none text-xs"
value={draft.description ?? ""}
placeholder={t("macros.descriptionPlaceholder")}
onChange={(event) =>
updateDraft({ ...draft, description: event.target.value })
}
/>
</div>
{draft.steps.length === 0 && (
<div className="border border-dashed border-border px-3 py-4 text-center text-[11px] text-muted-foreground">
{t("macros.noSteps")}
</div>
)}
<StepList
steps={draft.steps}
onChange={(steps) => updateDraft({ ...draft, steps })}
/>
<div className="flex items-center gap-2 border-t border-border pt-3">
<Button
size="sm"
variant="destructive"
className="rounded-none"
onClick={() => setPendingDelete(draft)}
>
<Trash2 className="mr-1 size-3" />
{t("macros.delete")}
</Button>
<Button
size="sm"
variant="outline"
className="rounded-none"
onClick={save}
>
<Save className="mr-1 size-3" />
{t("macros.save")}
</Button>
<Button
size="sm"
className="ml-auto rounded-none"
onClick={() =>
runningId === draft.id
? abortRef.current?.abort()
: requestRun(draft)
}
>
{runningId === draft.id ? (
<>
<Square className="mr-1 size-3" />
{t("macros.stop")}
</>
) : (
<>
<Play className="mr-1 size-3" />
{t("macros.run")}
</>
)}
</Button>
</div>
{dirty && (
<div className="text-[11px] text-amber-500">
{t("macros.unsaved")}
</div>
)}
</div>
)}
</div>
<AlertDialog
open={pendingDelete !== null}
onOpenChange={(open) => !open && setPendingDelete(null)}
>
<AlertDialogContent className="rounded-none">
<AlertDialogHeader>
<AlertDialogTitle>{t("macros.deleteTitle")}</AlertDialogTitle>
<AlertDialogDescription>
{t("macros.deleteBody", { name: pendingDelete?.name ?? "" })}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="rounded-none">
{t("macros.cancel")}
</AlertDialogCancel>
<AlertDialogAction className="rounded-none" onClick={confirmDelete}>
{t("macros.delete")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog
open={pendingRun !== null}
onOpenChange={(open) => !open && setPendingRun(null)}
>
<AlertDialogContent className="rounded-none">
<AlertDialogHeader>
<AlertDialogTitle>{t("macros.confirmRunTitle")}</AlertDialogTitle>
<AlertDialogDescription>
{t("macros.confirmRunBody", {
name: pendingRun?.name ?? "",
host: targetLabel,
})}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="rounded-none">
{t("macros.cancel")}
</AlertDialogCancel>
<AlertDialogAction
className="rounded-none"
onClick={() => {
const macro = pendingRun;
setPendingRun(null);
if (macro) {
confirmedRef.current.add(macro.id);
void execute(macro);
}
}}
>
{t("macros.confirmRunAction")}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}
@@ -0,0 +1,341 @@
export type MacroStep =
| { id: string; type: "send"; text: string; pressEnter: boolean }
| { id: string; type: "delay"; milliseconds: number }
| {
id: string;
type: "wait";
pattern: string;
isRegex?: boolean;
flags?: string;
timeoutMs: number;
onTimeout: "stop" | "continue";
}
| {
id: string;
type: "if";
pattern: string;
isRegex?: boolean;
flags?: string;
then: MacroStep[];
else: MacroStep[];
}
| { id: string; type: "repeat"; count: number; steps: MacroStep[] };
export interface TerminalMacro {
id: string;
name: string;
description?: string;
steps: MacroStep[];
createdAt: string;
updatedAt: string;
}
export interface MacroTerminalAdapter {
send(data: string): void;
subscribe(listener: (data: string) => void): () => void;
}
export type MacroRunEvent =
| { type: "step"; stepId: string }
| { type: "matched"; stepId: string; match: string }
| { type: "timeout"; stepId: string }
| { type: "complete" };
const MAX_OUTPUT = 100_000;
function bounded(value: number, minimum: number, maximum: number): number {
return Number.isFinite(value)
? Math.min(maximum, Math.max(minimum, Math.floor(value)))
: minimum;
}
function escapeLiteral(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function regex(pattern: string, flags = "", isRegex = true): RegExp {
const source = isRegex ? pattern : escapeLiteral(pattern);
return new RegExp(source, flags.replace(/[gy]/g, ""));
}
// Literal text is the default in the editor, so a bad regex should never kill a
// run. Fall back to matching the pattern literally instead.
function safeRegex(pattern: string, flags = "", isRegex = true): RegExp {
try {
return regex(pattern, flags, isRegex);
} catch {
return new RegExp(escapeLiteral(pattern), flags.replace(/[gy]/g, ""));
}
}
function abortError(): DOMException {
return new DOMException("Macro cancelled", "AbortError");
}
function delay(milliseconds: number, signal?: AbortSignal): Promise<void> {
if (signal?.aborted) return Promise.reject(abortError());
return new Promise((resolve, reject) => {
const timer = setTimeout(resolve, milliseconds);
signal?.addEventListener(
"abort",
() => {
clearTimeout(timer);
reject(abortError());
},
{ once: true },
);
});
}
export async function runTerminalMacro(
macro: TerminalMacro,
terminal: MacroTerminalAdapter,
options: {
signal?: AbortSignal;
onEvent?: (event: MacroRunEvent) => void;
} = {},
): Promise<void> {
let output = "";
let scanFrom = 0;
let wake: (() => void) | null = null;
const unsubscribe = terminal.subscribe((data) => {
const combined = output + data;
const removed = Math.max(0, combined.length - MAX_OUTPUT);
output = combined.slice(removed);
scanFrom = Math.max(0, scanFrom - removed);
wake?.();
});
const runSteps = async (steps: MacroStep[]): Promise<void> => {
for (const step of steps) {
if (options.signal?.aborted) throw abortError();
options.onEvent?.({ type: "step", stepId: step.id });
if (step.type === "send") {
scanFrom = output.length;
terminal.send(step.text + (step.pressEnter ? "\r" : ""));
continue;
}
if (step.type === "delay") {
await delay(bounded(step.milliseconds, 0, 300_000), options.signal);
continue;
}
if (step.type === "if") {
await runSteps(
safeRegex(step.pattern, step.flags, step.isRegex !== false).test(
output,
)
? step.then
: step.else,
);
continue;
}
if (step.type === "repeat") {
const count = bounded(step.count, 1, 100);
for (let index = 0; index < count; index += 1) {
await runSteps(step.steps);
}
continue;
}
const matcher = safeRegex(
step.pattern,
step.flags,
step.isRegex !== false,
);
const deadline = Date.now() + bounded(step.timeoutMs, 100, 300_000);
let match = matcher.exec(output.slice(scanFrom));
while (!match && Date.now() < deadline) {
await Promise.race([
new Promise<void>((resolve) => {
wake = resolve;
}),
delay(Math.min(250, deadline - Date.now()), options.signal),
]);
wake = null;
match = matcher.exec(output.slice(scanFrom));
}
if (match) {
scanFrom += match.index + match[0].length;
options.onEvent?.({
type: "matched",
stepId: step.id,
match: match[0],
});
} else {
options.onEvent?.({ type: "timeout", stepId: step.id });
if (step.onTimeout === "stop") {
throw new Error(`Timed out waiting for /${step.pattern}/`);
}
}
}
};
try {
await runSteps(macro.steps);
options.onEvent?.({ type: "complete" });
} finally {
unsubscribe();
}
}
/** Stored macros, bounded and cleaned; accepts the array or its JSON. */
export function sanitizeTerminalMacros(value: unknown): TerminalMacro[] {
if (value === null || value === undefined || value === "") return [];
try {
const parsed = typeof value === "string" ? JSON.parse(value) : value;
if (!Array.isArray(parsed)) return [];
let remainingSteps = 500;
const sanitizeSteps = (input: unknown, depth: number): MacroStep[] => {
if (!Array.isArray(input) || depth > 4 || remainingSteps <= 0) return [];
return input.flatMap((raw): MacroStep[] => {
if (!raw || typeof raw !== "object" || remainingSteps-- <= 0) return [];
const step = raw as Record<string, unknown>;
const stepId =
typeof step.id === "string" ? step.id : `step-${remainingSteps}`;
if (step.type === "send") {
return [
{
id: stepId,
type: "send",
text:
typeof step.text === "string" ? step.text.slice(0, 65_536) : "",
pressEnter: step.pressEnter !== false,
},
];
}
if (step.type === "delay") {
return [
{
id: stepId,
type: "delay",
milliseconds: bounded(Number(step.milliseconds), 0, 300_000),
},
];
}
if (step.type === "wait") {
return [
{
id: stepId,
type: "wait",
pattern:
typeof step.pattern === "string"
? step.pattern.slice(0, 1000)
: "",
isRegex: step.isRegex !== false,
flags:
typeof step.flags === "string"
? step.flags.replace(/[^imsu]/g, "")
: undefined,
timeoutMs: bounded(Number(step.timeoutMs), 100, 300_000),
onTimeout: step.onTimeout === "continue" ? "continue" : "stop",
},
];
}
if (step.type === "if") {
return [
{
id: stepId,
type: "if",
pattern:
typeof step.pattern === "string"
? step.pattern.slice(0, 1000)
: "",
isRegex: step.isRegex !== false,
flags:
typeof step.flags === "string"
? step.flags.replace(/[^imsu]/g, "")
: undefined,
then: sanitizeSteps(step.then, depth + 1),
else: sanitizeSteps(step.else, depth + 1),
},
];
}
if (step.type === "repeat") {
return [
{
id: stepId,
type: "repeat",
count: bounded(Number(step.count), 1, 100),
steps: sanitizeSteps(step.steps, depth + 1),
},
];
}
return [];
});
};
return parsed.slice(0, 100).flatMap((raw): TerminalMacro[] => {
if (!raw || typeof raw !== "object") return [];
const macro = raw as Record<string, unknown>;
if (typeof macro.id !== "string" || typeof macro.name !== "string")
return [];
const now = new Date().toISOString();
return [
{
id: macro.id,
name: macro.name.slice(0, 200),
description:
typeof macro.description === "string"
? macro.description.slice(0, 2000)
: undefined,
steps: sanitizeSteps(macro.steps, 0),
createdAt:
typeof macro.createdAt === "string" ? macro.createdAt : now,
updatedAt:
typeof macro.updatedAt === "string" ? macro.updatedAt : now,
},
];
});
} catch {
return [];
}
}
export function parseTerminalMacros(value?: string | null): TerminalMacro[] {
return sanitizeTerminalMacros(value);
}
/** Where 2.8 kept macros for a user whose storage mode was local. */
export const LOCAL_MACROS_KEY = "terminalMacros";
/**
* The saved macros plus any this browser kept on its own before 2.9.0 that
* the saved list does not already have, or null when there is nothing to
* move. `clear` removes the browser copy once the merged list is saved.
*/
export function mergeLocalMacros(
saved: TerminalMacro[],
storage: Pick<Storage, "getItem" | "removeItem"> | null = safeStorage(),
): { macros: TerminalMacro[]; clear: () => void } | null {
let raw: string | null = null;
try {
raw = storage?.getItem(LOCAL_MACROS_KEY) ?? null;
} catch {
return null;
}
if (raw === null) return null;
const clear = () => {
try {
storage?.removeItem(LOCAL_MACROS_KEY);
} catch {
// storage unavailable
}
};
const known = new Set(saved.map((macro) => macro.id));
const extra = sanitizeTerminalMacros(raw).filter(
(macro) => !known.has(macro.id),
);
if (extra.length === 0) {
clear();
return null;
}
return { macros: [...saved, ...extra].slice(0, 100), clear };
}
function safeStorage(): Storage | null {
try {
return typeof localStorage === "undefined" ? null : localStorage;
} catch {
return null;
}
}
@@ -8,7 +8,6 @@ export function HostTerminalSection({
setField,
updateForm,
host,
snippets,
}: HostEditorSectionProps) {
return (
<>
@@ -18,7 +17,6 @@ export function HostTerminalSection({
setField={setField}
updateForm={updateForm}
host={host}
snippets={snippets}
/>
</>
);
@@ -59,22 +59,16 @@ type HostPluginSettings = Record<string, Record<string, unknown>>;
/**
* The host editor's terminal appearance and behavior cards. The values are
* this plugin's host settings, kept on the editor form under pluginSettings
* and saved with the host; the startup snippet is still a core host field.
* and saved with the host.
*/
export function HostTerminalSettings({
form: editorForm,
setField: setCoreField,
updateForm,
host,
snippets: rawSnippets,
}: Pick<
HostEditorSectionProps,
"form" | "setField" | "updateForm" | "host" | "snippets"
>) {
}: Pick<HostEditorSectionProps, "form" | "setField" | "updateForm" | "host">) {
const { t } = useTranslation();
const client = useTerminalClientSettings();
const userSettings = useSettings("user");
const snippets = (rawSnippets ?? []) as { id: number; name: string }[];
const { setPreviewTerminalTheme } = useTabsSafe();
const stored = ((editorForm?.pluginSettings as HostPluginSettings)?.[
@@ -91,8 +85,7 @@ export function HostTerminalSettings({
const form = {
...own,
...(own.inheritAppearance ? userDefaults : {}),
startupSnippetId: (editorForm?.startupSnippetId ?? null) as number | null,
} as HostTerminalValues & { startupSnippetId: number | null };
} as HostTerminalValues;
const writeValues = (values: Record<string, unknown>) =>
updateForm((current) => {
@@ -883,30 +876,6 @@ export function HostTerminalSettings({
/>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 border-t border-border pt-4">
<div className="flex flex-col gap-1.5">
<label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
{t("hosts.startupSnippetLabel")}
</label>
<Select2
value={form.startupSnippetId ?? ""}
onChange={(e) =>
setCoreField(
"startupSnippetId",
e.target.value ? Number(e.target.value) : null,
)
}
className="flex h-9 w-full border border-border bg-background px-3 py-1 text-xs outline-none focus:ring-1 focus:ring-ring"
>
<option value="">{t("hosts.none")}</option>
{snippets.map((s) => (
<option key={s.id} value={s.id}>
{s.name}
</option>
))}
</Select2>
</div>
</div>
</div>
</SectionCard>
</>
@@ -74,7 +74,7 @@ import { toast } from "sonner";
import { Save } from "lucide-react";
import { TerminalToolbar } from "./TerminalToolbar.tsx";
import type { TerminalHandle, TerminalHostConfig } from "./terminal-types.ts";
import type { Host, Snippet, TabType } from "../types";
import type { Host, TabType } from "../types";
import { isTabKeyEvent } from "./terminal-key-event.ts";
import { installTouchWheelCoordinator } from "./touch-wheel-coordinator.ts";
import { loadTouchInputSettings } from "./touch-input-settings-store.ts";
@@ -85,6 +85,7 @@ import {
import { quoteTerminalImagePath } from "./terminal-image-path.ts";
import {
dispatchKeybindingAction,
isTerminalKeybindingAction,
sendRawToSocket,
} from "../lib/keybinding-dispatch";
import {
@@ -114,7 +115,6 @@ import {
Button,
hydrateLocalSharedHostAuth,
findMatchingKeybinding,
SnippetVariablesDialog,
type CustomKeybinding,
isElectron,
} from "@termix/plugin-sdk/ui";
@@ -124,6 +124,7 @@ import {
usePluginApi,
useSlotContributions,
getCustomKeybindings,
runKeybindingAction,
getClientPreference,
logActivity,
getHostPassword,
@@ -164,17 +165,6 @@ interface SSHTerminalProps {
onSessionReady?: (sessionId: string) => void;
}
/**
* The host's startup snippet. Core still keeps it in the host's terminal
* config until snippets owns it.
*/
function startupSnippetOf(host: object): number | null {
const config = (host as { terminalConfig?: { startupSnippetId?: unknown } })
.terminalConfig;
const id = Number(config?.startupSnippetId);
return Number.isInteger(id) && id > 0 ? id : null;
}
const ALTERNATE_SCREEN_SEQUENCE = /\x1b\[\?(47|1047|1049)([hl])/g;
function updateAlternateScreenMode(output: string, currentMode: boolean) {
@@ -291,11 +281,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
const terminalInputDisposableRef = useRef<{ dispose(): void } | null>(null);
const localEchoRef = useRef<TerminalLocalEcho | null>(null);
const customKeybindingsRef = useRef<CustomKeybinding[]>([]);
const [pendingKeybindingSnippet, setPendingKeybindingSnippet] = useState<{
id: string;
content: string;
appendEnter: boolean;
} | null>(null);
const resizeTimeout = useRef<NodeJS.Timeout | null>(null);
const wasDisconnectedBySSH = useRef(false);
const pingIntervalRef = useRef<NodeJS.Timeout | null>(null);
@@ -1966,7 +1951,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
| null
| undefined
)?.environmentVariables ?? [];
const startupSnippetId = startupSnippetOf(settingsHost);
if (environmentVariables.length > 0) {
for (const envVar of environmentVariables) {
@@ -1981,36 +1965,26 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
}
}
if (startupSnippetId) {
try {
const resolved = (await invokeAction(
"snippets.resolveForTerminal",
startupSnippetId,
{
ip: hostConfig.ip,
username: hostConfig.username,
port: hostConfig.port,
name: hostConfig.name,
},
)) as
| { needsInputs: boolean; content: string }
| null
| undefined;
if (
resolved &&
!resolved.needsInputs &&
ws.readyState === 1
) {
ws.send(
JSON.stringify({
type: "input",
data: resolved.content + "\n",
}),
);
}
} catch (err) {
console.warn("Failed to execute startup snippet:", err);
// The snippets plugin owns a host's startup command; while it
// is off nothing runs.
try {
const startup = await invokeAction(
"snippets.startupCommand",
settingsHost,
{
ip: hostConfig.ip,
username: hostConfig.username,
port: hostConfig.port,
name: hostConfig.name,
},
);
if (typeof startup === "string" && ws.readyState === 1) {
ws.send(
JSON.stringify({ type: "input", data: startup + "\n" }),
);
}
} catch (err) {
console.warn("Failed to run the startup command:", err);
}
if (settings.autoMosh && ws.readyState === 1) {
@@ -2988,26 +2962,30 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
e,
customKeybindingsRef.current,
);
if (matched) {
// The terminal's own actions run here; anything else goes to
// whoever registered it (the shell, or another plugin), and the key
// passes through while nothing does.
const handled =
!!matched &&
(isTerminalKeybindingAction(matched.action.type)
? dispatchKeybindingAction(matched.action, {
terminal,
webSocketRef,
writeTextToClipboard,
readTextFromClipboard,
})
: runKeybindingAction(matched.action, {
host: {
ip: hostConfig.ip,
username: hostConfig.username,
port: hostConfig.port,
name: hostConfig.name,
},
send: (data) => sendRawToSocket(webSocketRef, data),
}));
if (handled) {
e.preventDefault();
e.stopPropagation();
dispatchKeybindingAction(matched.action, {
terminal,
webSocketRef,
writeTextToClipboard,
readTextFromClipboard,
hostContext: {
ip: hostConfig.ip,
username: hostConfig.username,
port: hostConfig.port,
name: hostConfig.name,
},
onSnippetNeedsInputs: (snippet) =>
setPendingKeybindingSnippet({
...snippet,
appendEnter: matched.action.appendEnter !== false,
}),
});
return false;
}
}
@@ -3834,51 +3812,6 @@ const TerminalInner = forwardRef<TerminalHandle, SSHTerminalProps>(
/>
)}
{pendingKeybindingSnippet && (
<SnippetVariablesDialog
snippet={
{
id: 0,
name: t("newUi.sidebar.keybindings.actionRunSnippet"),
content: pendingKeybindingSnippet.content,
folder: null,
order: 0,
} as Snippet as never
}
host={{
ip: hostConfig.ip,
username: hostConfig.username,
port: hostConfig.port,
name: hostConfig.name,
}}
onCancel={() => setPendingKeybindingSnippet(null)}
onConfirm={(resolvedContent) => {
const appendEnter = pendingKeybindingSnippet.appendEnter;
setPendingKeybindingSnippet(null);
const send = () =>
sendRawToSocket(
webSocketRef,
resolvedContent + (appendEnter ? "\r" : ""),
);
const shouldConfirm =
localStorage.getItem("confirmSnippetExecution") === "true";
if (shouldConfirm) {
confirmWithToast(
t("newUi.sidebar.snippets.confirmRunMessage", {
name: t("newUi.sidebar.keybindings.actionRunSnippet"),
}),
send,
t("newUi.sidebar.snippets.confirmRunButton"),
t("newUi.sidebar.snippets.cancel"),
{ confirmOnEnter: true, duration: 6000 },
);
} else {
send();
}
}}
/>
)}
{tmuxSessionPicker && (
<TmuxSessionPicker
isOpen={true}
@@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { validateAdminSettings } from "../../src/backend/settings-validation.js";
import {
validateAdminSettings,
validateUserSettings,
} from "../../src/backend/settings-validation.js";
describe("validateAdminSettings", () => {
it("rejects relative image paths", () => {
@@ -19,3 +22,28 @@ describe("validateAdminSettings", () => {
).toEqual({});
});
});
describe("validateUserSettings", () => {
const macro = { id: "m", name: "M", steps: [] };
it("accepts bounded macros and a save without them", () => {
expect(validateUserSettings({ macros: [macro] })).toEqual({});
expect(validateUserSettings({ macros: JSON.stringify([macro]) })).toEqual(
{},
);
expect(validateUserSettings({ localEcho: "on" })).toEqual({});
});
it("refuses malformed or too many macros", () => {
expect(validateUserSettings({ macros: [{ id: 1 }] }).macros).toBeTruthy();
expect(validateUserSettings({ macros: "{" }).macros).toBeTruthy();
expect(
validateUserSettings({
macros: Array.from({ length: 101 }, (_, i) => ({
...macro,
id: `${i}`,
})),
}).macros,
).toBeTruthy();
});
});
@@ -1,10 +1,12 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { dispatchKeybindingAction } from "../../../src/frontend/lib/keybinding-dispatch";
import {
dispatchKeybindingAction,
isTerminalKeybindingAction,
validateSendControlCode,
validateSendText,
} from "../../../src/frontend/lib/keybinding-dispatch";
import type { Terminal } from "@xterm/xterm";
import type { KeybindingDispatchContext } from "../../../src/frontend/lib/keybinding-dispatch";
import * as sdkFrontend from "@termix/plugin-sdk/frontend";
vi.mock("@termix/plugin-sdk/frontend", () => ({ invokeAction: vi.fn() }));
function makeContext(
overrides: Partial<KeybindingDispatchContext> = {},
@@ -90,65 +92,36 @@ describe("dispatchKeybindingAction", () => {
expect(ctx.sentData).toEqual(["ls -la\r"]);
});
it("runSnippet resolves the snippet through the snippets.resolveForTerminal action and sends its content with a trailing return", async () => {
vi.mocked(sdkFrontend.invokeAction).mockResolvedValue({
needsInputs: false,
content: "uptime",
});
it("leaves any other action to whoever registered it", () => {
const ctx = makeContext();
dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
await Promise.resolve();
await Promise.resolve();
expect(sdkFrontend.invokeAction).toHaveBeenCalledWith(
"snippets.resolveForTerminal",
1,
null,
);
expect(ctx.sentData).toEqual(["uptime\r"]);
});
it("runSnippet does nothing when the snippet no longer exists", async () => {
vi.mocked(sdkFrontend.invokeAction).mockResolvedValue(undefined);
const ctx = makeContext();
dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
await Promise.resolve();
await Promise.resolve();
expect(
dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx),
).toBe(false);
expect(dispatchKeybindingAction({ type: "nextTab" }, ctx)).toBe(false);
expect(dispatchKeybindingAction({ type: "copy" }, ctx)).toBe(true);
expect(ctx.sentData).toEqual([]);
});
it("runSnippet passes hostContext through to the resolve action", async () => {
vi.mocked(sdkFrontend.invokeAction).mockResolvedValue({
needsInputs: false,
content: "ping 10.0.0.5 -p 22",
});
const ctx = makeContext({
hostContext: { ip: "10.0.0.5", username: "root", port: 22 },
});
dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
await Promise.resolve();
await Promise.resolve();
expect(sdkFrontend.invokeAction).toHaveBeenCalledWith(
"snippets.resolveForTerminal",
1,
{ ip: "10.0.0.5", username: "root", port: 22 },
it("knows which actions are its own", () => {
expect(isTerminalKeybindingAction("sendText")).toBe(true);
expect(isTerminalKeybindingAction("runSnippet")).toBe(false);
});
});
describe("keybinding validators", () => {
it("needs text to send", () => {
expect(validateSendText({ type: "sendText", text: " " })).toBe(
"keybindings.textRequired",
);
expect(ctx.sentData).toEqual(["ping 10.0.0.5 -p 22\r"]);
expect(validateSendText({ type: "sendText", text: "ls" })).toBeNull();
});
it("runSnippet defers to onSnippetNeedsInputs instead of sending when the action reports needsInputs", async () => {
vi.mocked(sdkFrontend.invokeAction).mockResolvedValue({
needsInputs: true,
content: "echo $INPUT_1",
});
const onSnippetNeedsInputs = vi.fn();
const ctx = makeContext({ onSnippetNeedsInputs });
dispatchKeybindingAction({ type: "runSnippet", snippetId: "1" }, ctx);
await Promise.resolve();
await Promise.resolve();
expect(ctx.sentData).toEqual([]);
expect(onSnippetNeedsInputs).toHaveBeenCalledWith({
id: "1",
content: "echo $INPUT_1",
});
it("needs a single letter for a control code", () => {
expect(
validateSendControlCode({ type: "sendControlCode", controlCode: "ab" }),
).toBe("keybindings.controlCodeRequired");
expect(
validateSendControlCode({ type: "sendControlCode", controlCode: "w" }),
).toBeNull();
});
});
@@ -0,0 +1,232 @@
import { describe, expect, it, vi } from "vitest";
import {
mergeLocalMacros,
parseTerminalMacros,
sanitizeTerminalMacros,
runTerminalMacro,
type MacroStep,
type TerminalMacro,
} from "../../../src/frontend/macros/terminal-macros";
function macro(steps: MacroStep[]): TerminalMacro {
return {
id: "macro-1",
name: "Test",
steps,
createdAt: "2026-01-01T00:00:00.000Z",
updatedAt: "2026-01-01T00:00:00.000Z",
};
}
describe("terminal macro runner", () => {
it("runs send, delay, regex wait, condition, and repeat steps", async () => {
vi.useFakeTimers();
const sent: string[] = [];
let listener = (_data: string) => {};
const run = runTerminalMacro(
macro([
{ id: "send", type: "send", text: "status", pressEnter: true },
{
id: "wait",
type: "wait",
pattern: "READY\\s+42",
timeoutMs: 1000,
onTimeout: "stop",
},
{
id: "if",
type: "if",
pattern: "READY",
then: [
{
id: "repeat",
type: "repeat",
count: 2,
steps: [
{ id: "ok", type: "send", text: "ok", pressEnter: false },
],
},
],
else: [],
},
]),
{
send: (data) => sent.push(data),
subscribe: (next) => {
listener = next;
return () => {};
},
},
);
await vi.advanceTimersByTimeAsync(1);
listener("READY 42");
await run;
expect(sent).toEqual(["status\r", "ok", "ok"]);
vi.useRealTimers();
});
it("stops when a required regex times out", async () => {
vi.useFakeTimers();
const run = runTerminalMacro(
macro([
{
id: "wait",
type: "wait",
pattern: "never",
timeoutMs: 100,
onTimeout: "stop",
},
]),
{ send: () => {}, subscribe: () => () => {} },
);
const rejection = expect(run).rejects.toThrow("Timed out");
await vi.advanceTimersByTimeAsync(101);
await rejection;
vi.useRealTimers();
});
it("sanitizes persisted macro bounds and drops unknown steps", () => {
const parsed = parseTerminalMacros(
JSON.stringify([
{
id: "m",
name: "Macro",
steps: [
{ id: "d", type: "delay", milliseconds: 999_999 },
{ id: "r", type: "repeat", count: 1000, steps: [] },
{ id: "bad", type: "execute-javascript", code: "nope" },
],
},
]),
);
expect(parsed[0].steps).toEqual([
{ id: "d", type: "delay", milliseconds: 300_000 },
{ id: "r", type: "repeat", count: 100, steps: [] },
]);
});
it("matches literal text without regex escaping when isRegex is false", async () => {
vi.useFakeTimers();
const sent: string[] = [];
let listener = (_data: string) => {};
const run = runTerminalMacro(
macro([
{
id: "wait",
type: "wait",
pattern: "progress (1/2)",
isRegex: false,
timeoutMs: 1000,
onTimeout: "stop",
},
{ id: "send", type: "send", text: "done", pressEnter: false },
]),
{
send: (data) => sent.push(data),
subscribe: (next) => {
listener = next;
return () => {};
},
},
);
await vi.advanceTimersByTimeAsync(1);
listener("progress (1/2)");
await run;
expect(sent).toEqual(["done"]);
vi.useRealTimers();
});
it("falls back to literal matching when a regex is invalid", async () => {
vi.useFakeTimers();
const sent: string[] = [];
let listener = (_data: string) => {};
const run = runTerminalMacro(
macro([
{
id: "wait",
type: "wait",
pattern: "oops(",
isRegex: true,
timeoutMs: 1000,
onTimeout: "stop",
},
{ id: "send", type: "send", text: "ok", pressEnter: false },
]),
{
send: (data) => sent.push(data),
subscribe: (next) => {
listener = next;
return () => {};
},
},
);
await vi.advanceTimersByTimeAsync(1);
listener("oops(");
await run;
expect(sent).toEqual(["ok"]);
vi.useRealTimers();
});
it("branches on literal if-step text", async () => {
const sent: string[] = [];
await runTerminalMacro(
macro([
{
id: "if",
type: "if",
pattern: "a.b",
isRegex: false,
then: [{ id: "y", type: "send", text: "yes", pressEnter: false }],
else: [{ id: "n", type: "send", text: "no", pressEnter: false }],
},
]),
{ send: (data) => sent.push(data), subscribe: () => () => {} },
);
// "a.b" as a literal must not match "axb"
expect(sent).toEqual(["no"]);
});
});
describe("mergeLocalMacros", () => {
function storage(value: string | null) {
const store = new Map<string, string>();
if (value !== null) store.set("terminalMacros", value);
return {
store,
getItem: (key: string) => store.get(key) ?? null,
removeItem: (key: string) => void store.delete(key),
};
}
const saved = (id: string): TerminalMacro => ({ ...macro([]), id });
it("returns null when the browser kept nothing", () => {
expect(mergeLocalMacros([saved("a")], storage(null))).toBeNull();
});
it("adds the browser's macros the saved list does not have", () => {
const local = storage(
JSON.stringify([
{ id: "a", name: "Dupe", steps: [] },
{ id: "b", name: "Local", steps: [] },
]),
);
const merged = mergeLocalMacros([saved("a")], local);
expect(merged?.macros.map((m) => m.id)).toEqual(["a", "b"]);
expect(local.store.has("terminalMacros")).toBe(true);
merged?.clear();
expect(local.store.has("terminalMacros")).toBe(false);
});
it("clears the key when everything is already saved", () => {
const local = storage(JSON.stringify([{ id: "a", name: "A", steps: [] }]));
expect(mergeLocalMacros([saved("a")], local)).toBeNull();
expect(local.store.has("terminalMacros")).toBe(false);
});
it("reads a stored array as well as its JSON", () => {
expect(
sanitizeTerminalMacros([{ id: "x", name: "X", steps: [] }]).map(
(m) => m.id,
),
).toEqual(["x"]);
});
});
@@ -30,7 +30,6 @@ function renderSettings(form: Form = { pluginSettings: {} }) {
setField={setField}
updateForm={updateForm}
host={{ id: "1", name: "box", ip: "10.0.0.1", port: 22 }}
snippets={[{ id: 7, name: "deploy" }]}
/>,
);
return { setField, updateForm, current: () => current };
@@ -56,13 +55,9 @@ describe("HostTerminalSettings", () => {
expect(screen.getByText("hosts.sudoPasswordAutoFillLabel")).toBeTruthy();
});
it("sets the startup snippet, a core host field, from the offered snippets", () => {
const { setField } = renderSettings();
const select = screen
.getByRole("option", { name: "deploy" })
.closest("select")!;
fireEvent.change(select, { target: { value: "7" } });
expect(setField).toHaveBeenCalledWith("startupSnippetId", 7);
it("no longer draws the startup snippet, which is the snippets plugin's", () => {
renderSettings();
expect(screen.queryByText("hosts.startupSnippetLabel")).toBeNull();
});
it("writes its values into the host's ssh-terminal settings", () => {