diff --git a/src/livecodes/i18n/locales/ar/language-info.lokalise.json b/src/livecodes/i18n/locales/ar/language-info.lokalise.json
index 74d4ff58b6..432a012e54 100644
--- a/src/livecodes/i18n/locales/ar/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/ar/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "مترجم V يعمل على WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": "
موقع V وثائق لغة V تعلم X في Y دقيقة، حيث X = V وثائق LiveCodes تحميل قالب البداية "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "مترجم VB.NET الذي يعمل على WebAssembly، باستخدام
Roslyn و
بيئة تشغيل .NET WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/ar/language-info.ts b/src/livecodes/i18n/locales/ar/language-info.ts
index 0da8da1c90..4a41e155fe 100644
--- a/src/livecodes/i18n/locales/ar/language-info.ts
+++ b/src/livecodes/i18n/locales/ar/language-info.ts
@@ -511,6 +511,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>الموقع الرسمي2> 1> <3> <4>وثائق TypeScript4> 3> <5> <6>تعلم X في Y دقائق، حيث X=TypeScript6> 5> <7> <8>تحميل القالب الأولي8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'مترجم V يعمل على WebAssembly.',
+ link: '<1> <2>موقع V2> 1> <3> <4>وثائق لغة V4> 3> <5> <6>تعلم X في Y دقيقة، حيث X = V6> 5> <7> <8>وثائق LiveCodes8> 7> <9> <10>تحميل قالب البداية10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'مترجم VB.NET الذي يعمل على WebAssembly، باستخدام <1>Roslyn1> و<2>بيئة تشغيل .NET WebAssembly2>',
link: '<1> <2>وثائق لغة Visual Basic2> 1> <3> <4> تعلم X في دقائق Y، حيث X = Visual Basic4> 3> <5> <6> وثائق LiveCodes6> 5> <7> <8> تحميل قالب البدء8> 7>',
diff --git a/src/livecodes/i18n/locales/ar/translation.lokalise.json b/src/livecodes/i18n/locales/ar/translation.lokalise.json
index a42cef5d1f..97fb2ed857 100644
--- a/src/livecodes/i18n/locales/ar/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/ar/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "قالب TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "قالب V"
+ },
"templates.starter.vb-wasm": {
"translation": "قالب VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/ar/translation.ts b/src/livecodes/i18n/locales/ar/translation.ts
index 62b9a1866a..028cd44c46 100644
--- a/src/livecodes/i18n/locales/ar/translation.ts
+++ b/src/livecodes/i18n/locales/ar/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'قالب Tcl',
teal: 'قالب Teal',
typescript: 'قالب TypeScript',
+ 'v-wasm': 'قالب V',
'vb-wasm': 'قالب VB.NET (Wasm)',
vue: 'قالب Vue',
vue2: 'قالب Vue 2',
diff --git a/src/livecodes/i18n/locales/bn/language-info.lokalise.json b/src/livecodes/i18n/locales/bn/language-info.lokalise.json
index 2209c6b338..31e0a34e22 100644
--- a/src/livecodes/i18n/locales/bn/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/bn/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "V কম্পাইলার ওয়েবঅ্যাসেম্বলিতে চলছে।"
+ },
+ "v-wasm.link": {
+ "translation": "
V ওয়েবসাইট V ল্যাঙ্গুয়েজ ডকুমেন্টেশন Y মিনিটে X শিখুন, যেখানে X=V LiveCodes ডকুমেন্টেশন স্টার্টার টেমপ্লেট লোড করুন "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "WebAssembly-তে চলমান VB.NET কম্পাইলার,
Roslyn এবং
.NET WebAssembly রানটাইম ব্যবহার করে"
},
diff --git a/src/livecodes/i18n/locales/bn/language-info.ts b/src/livecodes/i18n/locales/bn/language-info.ts
index e343eb9c07..8e1303e142 100644
--- a/src/livecodes/i18n/locales/bn/language-info.ts
+++ b/src/livecodes/i18n/locales/bn/language-info.ts
@@ -513,6 +513,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>অফিসিয়াল ওয়েবসাইট2> 1> <3> <4>TypeScript ডকুমেন্টেশন4> 3> <5> <6>Y মিনিটে X শিখুন, যেখানে X=TypeScript6> 5> <7> <8>স্টার্টার টেমপ্লেট লোড করুন8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'V কম্পাইলার ওয়েবঅ্যাসেম্বলিতে চলছে।',
+ link: '<1> <2>V ওয়েবসাইট2> 1> <3> <4>V ল্যাঙ্গুয়েজ ডকুমেন্টেশন4> 3> <5> <6>Y মিনিটে X শিখুন, যেখানে X=V6> 5> <7> <8>LiveCodes ডকুমেন্টেশন8> 7> <9> <10>স্টার্টার টেমপ্লেট লোড করুন10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'WebAssembly-তে চলমান VB.NET কম্পাইলার, <1>Roslyn1> এবং <2>.NET WebAssembly রানটাইম2> ব্যবহার করে',
link: '<1> <2>Visual Basic ভাষা ডকুমেন্টেশন2> 1> <3> <4>Y মিনিটে X শিখুন, যেখানে X=Visual Basic4> 3> <5> <6>LiveCodes ডকুমেন্টেশন6> 5> <7> <8>স্টার্টার টেমপ্লেট লোড করুন8> 7>',
diff --git a/src/livecodes/i18n/locales/bn/translation.lokalise.json b/src/livecodes/i18n/locales/bn/translation.lokalise.json
index 04e292fb20..06d31f9e53 100644
--- a/src/livecodes/i18n/locales/bn/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/bn/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript স্টার্টার"
},
+ "templates.starter.v-wasm": {
+ "translation": "V স্টার্টার"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (Wasm) স্টার্টার"
},
diff --git a/src/livecodes/i18n/locales/bn/translation.ts b/src/livecodes/i18n/locales/bn/translation.ts
index b903338c21..89f9b97d59 100644
--- a/src/livecodes/i18n/locales/bn/translation.ts
+++ b/src/livecodes/i18n/locales/bn/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl স্টার্টার',
teal: 'Teal স্টার্টার',
typescript: 'TypeScript স্টার্টার',
+ 'v-wasm': 'V স্টার্টার',
'vb-wasm': 'VB.NET (Wasm) স্টার্টার',
vue: 'Vue SFC স্টার্টার',
vue2: 'Vue 2 স্টার্টার',
diff --git a/src/livecodes/i18n/locales/de/language-info.lokalise.json b/src/livecodes/i18n/locales/de/language-info.lokalise.json
index e76c371e75..8551c7bd10 100644
--- a/src/livecodes/i18n/locales/de/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/de/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "V-Compiler, der auf WebAssembly läuft."
+ },
+ "v-wasm.link": {
+ "translation": "
V-Website V-Sprachdokumentation Lerne X in Y Minuten, wobei X = V LiveCodes-Dokumentation Starter-Vorlage laden "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "VB.NET-Compiler, der auf WebAssembly läuft, unter Verwendung von
Roslyn und der
.NET-WebAssembly-Laufzeitumgebung"
},
diff --git a/src/livecodes/i18n/locales/de/language-info.ts b/src/livecodes/i18n/locales/de/language-info.ts
index a4adf884fb..d58768ba3a 100644
--- a/src/livecodes/i18n/locales/de/language-info.ts
+++ b/src/livecodes/i18n/locales/de/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Offizielle Website2> 1> <3> <4>TypeScript-Dokumentation4> 3> <5> <6>Lerne X in Y Minuten, wobei X=TypeScript6> 5> <7> <8>Starter-Vorlage laden8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'V-Compiler, der auf WebAssembly läuft.',
+ link: '<1> <2>V-Website2> 1> <3> <4>V-Sprachdokumentation4> 3> <5> <6>Lerne X in Y Minuten, wobei X = V6> 5> <7> <8>LiveCodes-Dokumentation8> 7> <9> <10>Starter-Vorlage laden10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'VB.NET-Compiler, der auf WebAssembly läuft, unter Verwendung von <1>Roslyn1> und der <2>.NET-WebAssembly-Laufzeitumgebung2>',
link: '<1> <2>Dokumentation der Sprache Visual Basic2> 1> <3> <4> Lernen Sie X in Y Minuten, wobei X=Visual Basic4> 3> <5> <6> LiveCodes Dokumentation6> 5> <7> <8> Laden Sie die Startvorlage8> 7>',
diff --git a/src/livecodes/i18n/locales/de/translation.lokalise.json b/src/livecodes/i18n/locales/de/translation.lokalise.json
index 914ba2de33..9ad1cd6384 100644
--- a/src/livecodes/i18n/locales/de/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/de/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript-Starter"
},
+ "templates.starter.v-wasm": {
+ "translation": "V-Starter"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (Wasm)-Starter"
},
diff --git a/src/livecodes/i18n/locales/de/translation.ts b/src/livecodes/i18n/locales/de/translation.ts
index 915f79adf4..95cf87538c 100644
--- a/src/livecodes/i18n/locales/de/translation.ts
+++ b/src/livecodes/i18n/locales/de/translation.ts
@@ -1061,6 +1061,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl-Starter',
teal: 'Teal-Starter',
typescript: 'TypeScript-Starter',
+ 'v-wasm': 'V-Starter',
'vb-wasm': 'VB.NET (Wasm)-Starter',
vue: 'Vue-Starter',
vue2: 'Vue 2-Starter',
diff --git a/src/livecodes/i18n/locales/en/language-info.lokalise.json b/src/livecodes/i18n/locales/en/language-info.lokalise.json
index 987aaaa837..c15cb50fb3 100644
--- a/src/livecodes/i18n/locales/en/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/en/language-info.lokalise.json
@@ -1216,6 +1216,18 @@
"notes": "",
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "notes": "",
+ "translation": "V compiler running on WebAssembly."
+ },
+ "v-wasm.link": {
+ "notes": "###
###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n### ###\n\n\n",
+ "translation": " V website V language documentation Learn X in Y minutes, where X=V LiveCodes Documentation Load starter template "
+ },
+ "v-wasm.name": {
+ "notes": "",
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"notes": "### ###\n\n\n### ###\n\n\n",
"translation": "VB.NET compiler running on WebAssembly, using Roslyn and the .NET WebAssembly runtime"
diff --git a/src/livecodes/i18n/locales/en/language-info.ts b/src/livecodes/i18n/locales/en/language-info.ts
index 0dc6d293bf..3d9bf197ec 100644
--- a/src/livecodes/i18n/locales/en/language-info.ts
+++ b/src/livecodes/i18n/locales/en/language-info.ts
@@ -521,6 +521,11 @@ const languageInfo = {
link: '<1> <2>Official website2> 1> <3> <4>TypeScript documentation4> 3> <5> <6>Learn X in Y minutes, where X=TypeScript6> 5> <7> <8>Load starter template8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'V compiler running on WebAssembly.',
+ link: '<1> <2>V website2> 1> <3> <4>V language documentation4> 3> <5> <6>Learn X in Y minutes, where X=V6> 5> <7> <8>LiveCodes Documentation8> 7> <9> <10>Load starter template10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'VB.NET compiler running on WebAssembly, using <1>Roslyn1> and the <2>.NET WebAssembly runtime2>',
link: '<1> <2>Visual Basic language documentation2> 1> <3> <4>Learn X in Y minutes, where X=Visual Basic4> 3> <5> <6>LiveCodes Documentation6> 5> <7> <8>Load starter template8> 7>',
diff --git a/src/livecodes/i18n/locales/en/translation.lokalise.json b/src/livecodes/i18n/locales/en/translation.lokalise.json
index e6f2d89824..795b9e52f0 100644
--- a/src/livecodes/i18n/locales/en/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/en/translation.lokalise.json
@@ -2880,6 +2880,10 @@
"notes": "",
"translation": "TypeScript Starter"
},
+ "templates.starter.v-wasm": {
+ "notes": "",
+ "translation": "V Starter"
+ },
"templates.starter.vb-wasm": {
"notes": "",
"translation": "VB.NET (Wasm) Starter"
diff --git a/src/livecodes/i18n/locales/en/translation.ts b/src/livecodes/i18n/locales/en/translation.ts
index 89958f0bde..bb85d63c7f 100644
--- a/src/livecodes/i18n/locales/en/translation.ts
+++ b/src/livecodes/i18n/locales/en/translation.ts
@@ -1066,6 +1066,7 @@ const translation = {
tcl: 'Tcl Starter',
teal: 'Teal Starter',
typescript: 'TypeScript Starter',
+ 'v-wasm': 'V Starter',
'vb-wasm': 'VB.NET (Wasm) Starter',
vue: 'Vue SFC Starter',
vue2: 'Vue 2 Starter',
diff --git a/src/livecodes/i18n/locales/es/language-info.lokalise.json b/src/livecodes/i18n/locales/es/language-info.lokalise.json
index c4f43924b7..e09a488fc6 100644
--- a/src/livecodes/i18n/locales/es/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/es/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "Compilador V que se ejecuta en WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": " Página web de V Documentación del lenguaje V Aprende X en Y minutos, donde X = V Documentación de LiveCodes Cargar plantilla de inicio "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "Compilador de VB.NET que funciona con WebAssembly, utilizando Roslyn y el entorno de ejecución de .NET para WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/es/language-info.ts b/src/livecodes/i18n/locales/es/language-info.ts
index 1d3a0c5190..991e8c0690 100644
--- a/src/livecodes/i18n/locales/es/language-info.ts
+++ b/src/livecodes/i18n/locales/es/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Sitio web oficial2> 1> <3> <4>Documentación de TypeScript4> 3> <5> <6>Aprende X en Y minutos, donde X=TypeScript6> 5> <7> <8>Cargar plantilla inicial8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'Compilador V que se ejecuta en WebAssembly.',
+ link: '<1> <2>Página web de V2> 1> <3> <4>Documentación del lenguaje V4> 3> <5> <6>Aprende X en Y minutos, donde X = V6> 5> <7> <8>Documentación de LiveCodes8> 7> <9> <10>Cargar plantilla de inicio10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'Compilador de VB.NET que funciona con WebAssembly, utilizando <1>Roslyn1> y el <2>entorno de ejecución de .NET para WebAssembly2>',
link: '<1> <2>Documentación del lenguaje Visual Basic2> 1> <3> <4> Aprenda X en Y minutos, donde X=Visual Basic4> 3> <5> <6> Documentación de LiveCodes6> 5> <7> <8> Cargue la plantilla de inicio8> 7>',
diff --git a/src/livecodes/i18n/locales/es/translation.lokalise.json b/src/livecodes/i18n/locales/es/translation.lokalise.json
index 29545a4464..e8189c0806 100644
--- a/src/livecodes/i18n/locales/es/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/es/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "Inicio TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "Inicio V"
+ },
"templates.starter.vb-wasm": {
"translation": "Inicio VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/es/translation.ts b/src/livecodes/i18n/locales/es/translation.ts
index 97cabf6951..1ce9aec126 100644
--- a/src/livecodes/i18n/locales/es/translation.ts
+++ b/src/livecodes/i18n/locales/es/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'Inicio Tcl',
teal: 'Inicio Teal',
typescript: 'Inicio TypeScript',
+ 'v-wasm': 'Inicio V',
'vb-wasm': 'Inicio VB.NET (Wasm)',
vue: 'Inicio Vue',
vue2: 'Inicio Vue 2',
diff --git a/src/livecodes/i18n/locales/fa/language-info.lokalise.json b/src/livecodes/i18n/locales/fa/language-info.lokalise.json
index a64e8cd3c7..ab70c13dac 100644
--- a/src/livecodes/i18n/locales/fa/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/fa/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "کامپایلر V که روی WebAssembly اجرا میشود."
+ },
+ "v-wasm.link": {
+ "translation": " وبسایت V مستندات زبان V زبان V را در چند دقیقه بیاموزید مستندات LiveCodes بارگذاری قالب شروع "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "کامپایلر VB.NET که روی WebAssembly اجرا میشود، با استفاده از Roslyn و محیط اجرایی .NET WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/fa/language-info.ts b/src/livecodes/i18n/locales/fa/language-info.ts
index dea2e9dc89..83e21e8287 100644
--- a/src/livecodes/i18n/locales/fa/language-info.ts
+++ b/src/livecodes/i18n/locales/fa/language-info.ts
@@ -514,6 +514,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>وبسایت رسمی2> 1> <3> <4>مستندات TypeScript4> 3> <5> <6>یادگیری X در Y دقیقه، جایی که X=TypeScript6> 5> <7> <8>بارگذاری قالب شروع کننده8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'کامپایلر V که روی WebAssembly اجرا میشود.',
+ link: '<1> <2>وبسایت V2> 1> <3> <4>مستندات زبان V4> 3> <5> <6>زبان V را در چند دقیقه بیاموزید6> 5> <7> <8>مستندات LiveCodes8> 7> <9> <10>بارگذاری قالب شروع10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'کامپایلر VB.NET که روی WebAssembly اجرا میشود، با استفاده از <1>Roslyn1> و <2>محیط اجرایی .NET WebAssembly2>',
link: '<1> <2>مستندات زبان Visual Basic2> 1> <3> <4>یادگیری X در Y دقیقه، جایی که X=Visual Basic4> 3> <5> <6>مستندات LiveCodes6> 5> <7> <8>بارگذاری قالب شروع کننده8> 7>',
diff --git a/src/livecodes/i18n/locales/fa/translation.lokalise.json b/src/livecodes/i18n/locales/fa/translation.lokalise.json
index 344ad63c46..ad42e57b33 100644
--- a/src/livecodes/i18n/locales/fa/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/fa/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "شروع کننده تایپاسکریپت"
},
+ "templates.starter.v-wasm": {
+ "translation": "شروع کننده V"
+ },
"templates.starter.vb-wasm": {
"translation": "شروع کننده VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/fa/translation.ts b/src/livecodes/i18n/locales/fa/translation.ts
index f51ed44098..f70134087e 100644
--- a/src/livecodes/i18n/locales/fa/translation.ts
+++ b/src/livecodes/i18n/locales/fa/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'شروع کننده Tcl',
teal: 'شروع کننده Teal',
typescript: 'شروع کننده تایپاسکریپت',
+ 'v-wasm': 'شروع کننده V',
'vb-wasm': 'شروع کننده VB.NET (Wasm)',
vue: 'شروع کننده Vue SFC',
vue2: 'شروع کننده Vue 2',
diff --git a/src/livecodes/i18n/locales/fr/language-info.lokalise.json b/src/livecodes/i18n/locales/fr/language-info.lokalise.json
index 140c8026a2..84a34baff5 100644
--- a/src/livecodes/i18n/locales/fr/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/fr/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "Compilateur V fonctionnant sur WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": " Site web de V Documentation du langage V Apprenez X en Y minutes, où X = V Documentation LiveCodes Charger un modèle de démarrage "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "Compilateur VB.NET fonctionnant sur WebAssembly, utilisant Roslyn et le runtime .NET WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/fr/language-info.ts b/src/livecodes/i18n/locales/fr/language-info.ts
index e9328b4798..d47d0cda0a 100644
--- a/src/livecodes/i18n/locales/fr/language-info.ts
+++ b/src/livecodes/i18n/locales/fr/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Site officiel2> 1> <3> <4>Documentation TypeScript4> 3> <5> <6>Apprenez X en Y minutes, où X=TypeScript6> 5> <7> <8>Charger le modèle de démarrage8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'Compilateur V fonctionnant sur WebAssembly.',
+ link: '<1> <2>Site web de V2> 1> <3> <4>Documentation du langage V4> 3> <5> <6>Apprenez X en Y minutes, où X = V6> 5> <7> <8>Documentation LiveCodes8> 7> <9> <10>Charger un modèle de démarrage10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'Compilateur VB.NET fonctionnant sur WebAssembly, utilisant <1>Roslyn1> et le <2>runtime .NET WebAssembly2>',
link: '<1> <2>Documentation sur le langage Visual Basic2> 1> <3> <4> Apprenez X en Y minutes, où X=Visual Basic4> 3> <5> <6> Documentation LiveCodes6> 5> <7> <8> Charger le modèle de démarrage8> 7>',
diff --git a/src/livecodes/i18n/locales/fr/translation.lokalise.json b/src/livecodes/i18n/locales/fr/translation.lokalise.json
index 4135e48ffd..1db7fa2a52 100644
--- a/src/livecodes/i18n/locales/fr/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/fr/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "Démarrage TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "Démarrage V"
+ },
"templates.starter.vb-wasm": {
"translation": "Démarrage VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/fr/translation.ts b/src/livecodes/i18n/locales/fr/translation.ts
index 6d083ee2b2..5424a4a20d 100644
--- a/src/livecodes/i18n/locales/fr/translation.ts
+++ b/src/livecodes/i18n/locales/fr/translation.ts
@@ -1061,6 +1061,7 @@ const translation: I18nTranslation = {
tcl: 'Démarrage Tcl',
teal: 'Démarrage Teal',
typescript: 'Démarrage TypeScript',
+ 'v-wasm': 'Démarrage V',
'vb-wasm': 'Démarrage VB.NET (Wasm)',
vue: 'Démarrage Vue',
vue2: 'Démarrage Vue 2',
diff --git a/src/livecodes/i18n/locales/hi/language-info.lokalise.json b/src/livecodes/i18n/locales/hi/language-info.lokalise.json
index f280849b4d..1f0ec67905 100644
--- a/src/livecodes/i18n/locales/hi/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/hi/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "वेबअसेंबली पर चल रहा V कंपाइलर।"
+ },
+ "v-wasm.link": {
+ "translation": " V वेबसाइट V भाषा दस्तावेज़ीकरण वाई मिनटों में एक्स सीखें, जहां एक्स=V लाइवकोड दस्तावेज़ीकरण स्टार्टर टेम्पलेट लोड करें "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "VB.NET कंपाइलर WebAssembly पर चल रहा है, Roslyn और .NET वेबअसेंबली रनटाइम का उपयोग करके"
},
diff --git a/src/livecodes/i18n/locales/hi/language-info.ts b/src/livecodes/i18n/locales/hi/language-info.ts
index 2f6cb47781..e5cfcd00e1 100644
--- a/src/livecodes/i18n/locales/hi/language-info.ts
+++ b/src/livecodes/i18n/locales/hi/language-info.ts
@@ -516,6 +516,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>आधिकारिक वेबसाइट2> 1> <3> <4>TypeScript दस्तावेज़ीकरण4> 3> <5> <6>X को Y मिनटों में सीखें, जहां X=TypeScript6> 5> <7> <8>स्टार्टर टेम्पलेट लोड करें8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'वेबअसेंबली पर चल रहा V कंपाइलर।',
+ link: '<1> <2>V वेबसाइट2> 1> <3> <4>V भाषा दस्तावेज़ीकरण4> 3> <5> <6>वाई मिनटों में एक्स सीखें, जहां एक्स=V6> 5> <7> <8>लाइवकोड दस्तावेज़ीकरण8> 7> <9> <10>स्टार्टर टेम्पलेट लोड करें10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'VB.NET कंपाइलर WebAssembly पर चल रहा है, <1>Roslyn1> और <2>.NET वेबअसेंबली रनटाइम2> का उपयोग करके',
link: '<1> <2>Visual Basic भाषा दस्तावेज़ीकरण2> 1> <3> <4>एक्स को वाई मिनटों में सीखें, जहां एक्स=Visual Basic4> 3> <5> <6>लाइवकोड दस्तावेज़ीकरण6> 5> <7> <8>स्टार्टर टेम्पलेट लोड करें8> 7>',
diff --git a/src/livecodes/i18n/locales/hi/translation.lokalise.json b/src/livecodes/i18n/locales/hi/translation.lokalise.json
index c05ad0a63e..e9f6673e51 100644
--- a/src/livecodes/i18n/locales/hi/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/hi/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript स्टार्टर"
},
+ "templates.starter.v-wasm": {
+ "translation": "V स्टार्टर"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (Wasm) स्टार्टर"
},
diff --git a/src/livecodes/i18n/locales/hi/translation.ts b/src/livecodes/i18n/locales/hi/translation.ts
index 2120fab325..a869e4b1ee 100644
--- a/src/livecodes/i18n/locales/hi/translation.ts
+++ b/src/livecodes/i18n/locales/hi/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl स्टार्टर',
teal: 'Teal स्टार्टर',
typescript: 'TypeScript स्टार्टर',
+ 'v-wasm': 'V स्टार्टर',
'vb-wasm': 'VB.NET (Wasm) स्टार्टर',
vue: 'Vue स्टार्टर',
vue2: 'Vue 2 स्टार्टर',
diff --git a/src/livecodes/i18n/locales/hu/language-info.lokalise.json b/src/livecodes/i18n/locales/hu/language-info.lokalise.json
index 84eac14e02..15debc8b58 100644
--- a/src/livecodes/i18n/locales/hu/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/hu/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "A V fordító WebAssembly-en fut."
+ },
+ "v-wasm.link": {
+ "translation": " A V weboldala A V nyelv dokumentációja Tanulj meg X-et Y perc alatt, ahol X=V LiveCodes dokumentáció Indító sablon betöltése "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "VB.NET fordító WebAssembly-n futva, a Roslyn és a .NET WebAssembly futtatókörnyezet használatával"
},
diff --git a/src/livecodes/i18n/locales/hu/language-info.ts b/src/livecodes/i18n/locales/hu/language-info.ts
index 6e8d4606e2..107be26215 100644
--- a/src/livecodes/i18n/locales/hu/language-info.ts
+++ b/src/livecodes/i18n/locales/hu/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Hivatalos weboldal2> 1> <3> <4>TypeScript dokumentáció4> 3> <5> <6>Tanulj X-et Y perc alatt, ahol X=TypeScript6> 5> <7> <8>Kezdő sablon betöltése8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'A V fordító WebAssembly-en fut.',
+ link: '<1> <2>A V weboldala2> 1> <3> <4>A V nyelv dokumentációja4> 3> <5> <6>Tanulj meg X-et Y perc alatt, ahol X=V6> 5> <7> <8>LiveCodes dokumentáció8> 7> <9> <10>Indító sablon betöltése10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'VB.NET fordító WebAssembly-n futva, a <1>Roslyn1> és a <2>.NET WebAssembly futtatókörnyezet2> használatával',
link: '<1> <2>Visual Basic nyelvi dokumentáció2> 1> <3> <4>Tanulj X-et Y perc alatt, ahol X=Visual Basic4> 3> <5> <6>LiveCodes dokumentáció6> 5> <7> <8>Kezdő sablon betöltése8> 7>',
diff --git a/src/livecodes/i18n/locales/hu/translation.lokalise.json b/src/livecodes/i18n/locales/hu/translation.lokalise.json
index f46a2869c1..111fde9cd6 100644
--- a/src/livecodes/i18n/locales/hu/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/hu/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript kezdő"
},
+ "templates.starter.v-wasm": {
+ "translation": "V kezdő"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (Wasm) kezdő"
},
diff --git a/src/livecodes/i18n/locales/hu/translation.ts b/src/livecodes/i18n/locales/hu/translation.ts
index dcb1301048..96feadcee3 100644
--- a/src/livecodes/i18n/locales/hu/translation.ts
+++ b/src/livecodes/i18n/locales/hu/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl kezdő',
teal: 'Teal kezdő',
typescript: 'TypeScript kezdő',
+ 'v-wasm': 'V kezdő',
'vb-wasm': 'VB.NET (Wasm) kezdő',
vue: 'Vue SFC kezdő',
vue2: 'Vue 2 kezdő',
diff --git a/src/livecodes/i18n/locales/id/language-info.lokalise.json b/src/livecodes/i18n/locales/id/language-info.lokalise.json
index c5a075af70..de44c91fd2 100644
--- a/src/livecodes/i18n/locales/id/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/id/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "Kompiler V yang berjalan di WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": " Situs web V Dokumentasi bahasa V Pelajari X dalam Y menit, di mana X = V Dokumentasi LiveCodes Muat templat awal "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "Kompiler VB.NET berjalan di WebAssembly, menggunakan Roslyn dan runtime .NET WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/id/language-info.ts b/src/livecodes/i18n/locales/id/language-info.ts
index 9a24feb09e..cbc949f82e 100644
--- a/src/livecodes/i18n/locales/id/language-info.ts
+++ b/src/livecodes/i18n/locales/id/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Situs web resmi2> 1> <3> <4>Dokumentasi TypeScript4> 3> <5> <6>Pelajari X dalam Y menit, dengan X=TypeScript6> 5> <7> <8>Muat template pemula8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'Kompiler V yang berjalan di WebAssembly.',
+ link: '<1> <2>Situs web V2> 1> <3> <4>Dokumentasi bahasa V4> 3> <5> <6>Pelajari X dalam Y menit, di mana X = V6> 5> <7> <8>Dokumentasi LiveCodes8> 7> <9> <10>Muat templat awal10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'Kompiler VB.NET berjalan di WebAssembly, menggunakan <1>Roslyn1> dan <2>runtime .NET WebAssembly2>',
link: '<1> <2>Dokumentasi bahasa Visual Basic2> 1> <3> <4>Pelajari X dalam Y menit, dengan X=Visual Basic4> 3> <5> <6>Dokumentasi LiveCodes6> 5> <7> <8>Muat template pemula8> 7>',
diff --git a/src/livecodes/i18n/locales/id/translation.lokalise.json b/src/livecodes/i18n/locales/id/translation.lokalise.json
index 6d0aa7c2be..b5a00bac5f 100644
--- a/src/livecodes/i18n/locales/id/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/id/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "Pemula TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "Pemula V"
+ },
"templates.starter.vb-wasm": {
"translation": "Pemula VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/id/translation.ts b/src/livecodes/i18n/locales/id/translation.ts
index ad70da0152..1a67e1e656 100644
--- a/src/livecodes/i18n/locales/id/translation.ts
+++ b/src/livecodes/i18n/locales/id/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'Pemula Tcl',
teal: 'Pemula Teal',
typescript: 'Pemula TypeScript',
+ 'v-wasm': 'Pemula V',
'vb-wasm': 'Pemula VB.NET (Wasm)',
vue: 'Pemula Vue SFC',
vue2: 'Pemula Vue 2',
diff --git a/src/livecodes/i18n/locales/it/language-info.lokalise.json b/src/livecodes/i18n/locales/it/language-info.lokalise.json
index 34bba3d623..33d9f87e58 100644
--- a/src/livecodes/i18n/locales/it/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/it/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "Compilatore V in esecuzione su WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": " Sito web di V Documentazione sul linguaggio V Impara X in Y minuti, dove X = V Documentazione LiveCodes Carica il modello di avvio "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "Compilatore VB.NET in esecuzione su WebAssembly, utilizzando Roslyn e il runtime .NET per WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/it/language-info.ts b/src/livecodes/i18n/locales/it/language-info.ts
index 09bad4dc6b..10bb9e2544 100644
--- a/src/livecodes/i18n/locales/it/language-info.ts
+++ b/src/livecodes/i18n/locales/it/language-info.ts
@@ -514,6 +514,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Sito ufficiale2> 1> <3> <4>Documentazione di TypeScript4> 3> <5> <6>Impara X in Y minuti, dove X=TypeScript6> 5> <7> <8>Carica modello di partenza8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'Compilatore V in esecuzione su WebAssembly.',
+ link: '<1> <2>Sito web di V2> 1> <3> <4>Documentazione sul linguaggio V4> 3> <5> <6>Impara X in Y minuti, dove X = V6> 5> <7> <8>Documentazione LiveCodes8> 7> <9> <10>Carica il modello di avvio10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'Compilatore VB.NET in esecuzione su WebAssembly, utilizzando <1>Roslyn1> e il <2>runtime .NET per WebAssembly2>',
link: '<1> <2>Documentazione sul linguaggio Visual Basic2> 1> <3> <4> Imparare X in Y minuti, dove X=Visual Basic4> 3> <5> <6> Documentazione LiveCodes6> 5> <7> <8> Carica il modello iniziale8> 7>',
diff --git a/src/livecodes/i18n/locales/it/translation.lokalise.json b/src/livecodes/i18n/locales/it/translation.lokalise.json
index 816e747b3a..62b8230178 100644
--- a/src/livecodes/i18n/locales/it/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/it/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "Starter TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "Starter V"
+ },
"templates.starter.vb-wasm": {
"translation": "Starter VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/it/translation.ts b/src/livecodes/i18n/locales/it/translation.ts
index 00d5a12ec9..136413ab2d 100644
--- a/src/livecodes/i18n/locales/it/translation.ts
+++ b/src/livecodes/i18n/locales/it/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'Starter Tcl',
teal: 'Starter Teal',
typescript: 'Starter TypeScript',
+ 'v-wasm': 'Starter V',
'vb-wasm': 'Starter VB.NET (Wasm)',
vue: 'Starter Vue',
vue2: 'Starter Vue 2',
diff --git a/src/livecodes/i18n/locales/ja/language-info.lokalise.json b/src/livecodes/i18n/locales/ja/language-info.lokalise.json
index 1b50161e6a..b6cc53d4b3 100644
--- a/src/livecodes/i18n/locales/ja/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/ja/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "WebAssembly上で動作するVコンパイラ。"
+ },
+ "v-wasm.link": {
+ "translation": " Vのウェブサイト V言語のドキュメント 「XをY分で学ぶ」(X=V) LiveCodes ドキュメント スターターテンプレートを読み込む "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "WebAssembly 上で動作する VB.NET コンパイラ。Roslyn と .NET WebAssembly ランタイム を使用。"
},
diff --git a/src/livecodes/i18n/locales/ja/language-info.ts b/src/livecodes/i18n/locales/ja/language-info.ts
index eb625fdb27..7310680931 100644
--- a/src/livecodes/i18n/locales/ja/language-info.ts
+++ b/src/livecodes/i18n/locales/ja/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>公式ウェブサイト2> 1> <3> <4>TypeScript ドキュメント4> 3> <5> <6>X分でYを学ぶ、X=TypeScript6> 5> <7> <8>スターターテンプレートを読み込む8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'WebAssembly上で動作するVコンパイラ。',
+ link: '<1> <2>Vのウェブサイト2> 1> <3> <4>V言語のドキュメント4> 3> <5> <6>「XをY分で学ぶ」(X=V)6> 5> <7> <8>LiveCodes ドキュメント8> 7> <9> <10>スターターテンプレートを読み込む10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'WebAssembly 上で動作する VB.NET コンパイラ。<1>Roslyn1> と <2>.NET WebAssembly ランタイム2> を使用。',
link: '<1> <2>Visual Basic 言語のドキュメント2> 1> <3> <4>X を Y 分で学習します (X=Visual Basic の場合)4> 3> <5> <6>LiveCodesドキュメンテーション6> 5> <7> <8>スターターテンプレートをロードする8> 7>',
diff --git a/src/livecodes/i18n/locales/ja/translation.lokalise.json b/src/livecodes/i18n/locales/ja/translation.lokalise.json
index 43ae4dbe59..46230746f0 100644
--- a/src/livecodes/i18n/locales/ja/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/ja/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScriptスターター"
},
+ "templates.starter.v-wasm": {
+ "translation": "Vスターター"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET(Wasm)スターター"
},
diff --git a/src/livecodes/i18n/locales/ja/translation.ts b/src/livecodes/i18n/locales/ja/translation.ts
index 152287864c..b4b308f466 100644
--- a/src/livecodes/i18n/locales/ja/translation.ts
+++ b/src/livecodes/i18n/locales/ja/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'Tclスターター',
teal: 'Tealスターター',
typescript: 'TypeScriptスターター',
+ 'v-wasm': 'Vスターター',
'vb-wasm': 'VB.NET(Wasm)スターター',
vue: 'Vue スターター',
vue2: 'Vue 2スターター',
diff --git a/src/livecodes/i18n/locales/nl/language-info.lokalise.json b/src/livecodes/i18n/locales/nl/language-info.lokalise.json
index 1f5f1f62f6..57886e5b0d 100644
--- a/src/livecodes/i18n/locales/nl/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/nl/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "V-compiler die op WebAssembly draait."
+ },
+ "v-wasm.link": {
+ "translation": " V-website Documentatie over de taal V Leer X in Y minuten, waarbij X = V LiveCodes-documentatie Starterssjabloon laden "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "VB.NET-compiler draaiend op WebAssembly, met Roslyn en de .NET WebAssembly-runtime"
},
diff --git a/src/livecodes/i18n/locales/nl/language-info.ts b/src/livecodes/i18n/locales/nl/language-info.ts
index e1f714ea1c..c15a2940a5 100644
--- a/src/livecodes/i18n/locales/nl/language-info.ts
+++ b/src/livecodes/i18n/locales/nl/language-info.ts
@@ -514,6 +514,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Officiële website2> 1> <3> <4>TypeScript documentatie4> 3> <5> <6>Leer X in Y minuten, waarbij X=TypeScript6> 5> <7> <8>Startsjabloon laden8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'V-compiler die op WebAssembly draait.',
+ link: '<1> <2>V-website2> 1> <3> <4>Documentatie over de taal V4> 3> <5> <6>Leer X in Y minuten, waarbij X = V6> 5> <7> <8>LiveCodes-documentatie8> 7> <9> <10>Starterssjabloon laden10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'VB.NET-compiler draaiend op WebAssembly, met <1>Roslyn1> en de <2>.NET WebAssembly-runtime2>',
link: '<1> <2>Visual Basic-taaldocumentatie2> 1> <3> <4>Leer X in Y minuten, waarbij X=Visual Basic4> 3> <5> <6>LiveCodes Documentatie6> 5> <7> <8>Startsjabloon laden8> 7>',
diff --git a/src/livecodes/i18n/locales/nl/translation.lokalise.json b/src/livecodes/i18n/locales/nl/translation.lokalise.json
index 1a2cf5bea2..0f1427d739 100644
--- a/src/livecodes/i18n/locales/nl/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/nl/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript-starter"
},
+ "templates.starter.v-wasm": {
+ "translation": "V-starter"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (Wasm)-starter"
},
diff --git a/src/livecodes/i18n/locales/nl/translation.ts b/src/livecodes/i18n/locales/nl/translation.ts
index 7aac8a6a1a..525256982c 100644
--- a/src/livecodes/i18n/locales/nl/translation.ts
+++ b/src/livecodes/i18n/locales/nl/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl-starter',
teal: 'Teal-starter',
typescript: 'TypeScript-starter',
+ 'v-wasm': 'V-starter',
'vb-wasm': 'VB.NET (Wasm)-starter',
vue: 'Vue SFC-starter',
vue2: 'Vue 2-starter',
diff --git a/src/livecodes/i18n/locales/pt/language-info.lokalise.json b/src/livecodes/i18n/locales/pt/language-info.lokalise.json
index 32cd91fd32..5cd84d86f5 100644
--- a/src/livecodes/i18n/locales/pt/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/pt/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "Compilador V a funcionar no WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": " Site do V Documentação da linguagem V Aprenda X em Y minutos, em que X = V Documentação do LiveCodes Carregar modelo inicial "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "Compilador VB.NET executado no WebAssembly, usando Roslyn e o ambiente de execução .NET para WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/pt/language-info.ts b/src/livecodes/i18n/locales/pt/language-info.ts
index a79cf64a9c..7366e7d092 100644
--- a/src/livecodes/i18n/locales/pt/language-info.ts
+++ b/src/livecodes/i18n/locales/pt/language-info.ts
@@ -514,6 +514,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Site oficial2> 1> <3> <4>Documentação do TypeScript4> 3> <5> <6>Aprenda X em Y minutos, onde X=TypeScript6> 5> <7> <8>Carregar modelo inicial8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'Compilador V a funcionar no WebAssembly.',
+ link: '<1> <2>Site do V2> 1> <3> <4>Documentação da linguagem V4> 3> <5> <6>Aprenda X em Y minutos, em que X = V6> 5> <7> <8>Documentação do LiveCodes8> 7> <9> <10>Carregar modelo inicial10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'Compilador VB.NET executado no WebAssembly, usando <1>Roslyn1> e o <2>ambiente de execução .NET para WebAssembly2>',
link: '<1> <2>Documentação da linguagem Visual Basic2> 1> <3> <4> Aprenda X em Y minutos, onde X=Visual Basic4> 3> <5> <6> Documentação LiveCodes6> 5> <7> <8> Carregar modelo inicial8> 7>',
diff --git a/src/livecodes/i18n/locales/pt/translation.lokalise.json b/src/livecodes/i18n/locales/pt/translation.lokalise.json
index 9240d000f2..6b8a7642c3 100644
--- a/src/livecodes/i18n/locales/pt/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/pt/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "Iniciante TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "Iniciante V"
+ },
"templates.starter.vb-wasm": {
"translation": "Iniciante VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/pt/translation.ts b/src/livecodes/i18n/locales/pt/translation.ts
index e068a9419b..85ab70f0cc 100644
--- a/src/livecodes/i18n/locales/pt/translation.ts
+++ b/src/livecodes/i18n/locales/pt/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'Iniciante Tcl',
teal: 'Iniciante Teal',
typescript: 'Iniciante TypeScript',
+ 'v-wasm': 'Iniciante V',
'vb-wasm': 'Iniciante VB.NET (Wasm)',
vue: 'Iniciante Vue',
vue2: 'Iniciante Vue 2',
diff --git a/src/livecodes/i18n/locales/ru/language-info.lokalise.json b/src/livecodes/i18n/locales/ru/language-info.lokalise.json
index 36f603eb61..e6afaab961 100644
--- a/src/livecodes/i18n/locales/ru/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/ru/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "Компилятор V, работающий на WebAssembly."
+ },
+ "v-wasm.link": {
+ "translation": " Сайт V Документация по языку V Изучите X за Y минут, где X = V Документация LiveCodes Загрузить стартовый шаблон "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "Компилятор VB.NET, работающий на WebAssembly, с использованием Roslyn и среды выполнения .NET для WebAssembly"
},
diff --git a/src/livecodes/i18n/locales/ru/language-info.ts b/src/livecodes/i18n/locales/ru/language-info.ts
index b40fa609d1..7f44eae380 100644
--- a/src/livecodes/i18n/locales/ru/language-info.ts
+++ b/src/livecodes/i18n/locales/ru/language-info.ts
@@ -516,6 +516,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Официальный сайт2> 1> <3> <4>Документация TypeScript4> 3> <5> <6>Изучите X за Y минут, где X=TypeScript6> 5> <7> <8>Загрузить стартовый шаблон8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'Компилятор V, работающий на WebAssembly.',
+ link: '<1> <2>Сайт V2> 1> <3> <4>Документация по языку V4> 3> <5> <6>Изучите X за Y минут, где X = V6> 5> <7> <8>Документация LiveCodes8> 7> <9> <10>Загрузить стартовый шаблон10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'Компилятор VB.NET, работающий на WebAssembly, с использованием <1>Roslyn1> и <2>среды выполнения .NET для WebAssembly2>',
link: '<1> <2>Документация по языку Visual Basic2> 1> <3> <4> Узнайте X за Y минут, где X=Visual Basic4> 3> <5> <6> Документация LiveCodes6> 5> <7> <8> Загрузите стартовый шаблон8> 7>',
diff --git a/src/livecodes/i18n/locales/ru/translation.lokalise.json b/src/livecodes/i18n/locales/ru/translation.lokalise.json
index 4a0004d386..4d10a6cc62 100644
--- a/src/livecodes/i18n/locales/ru/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/ru/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "Стартер TypeScript"
},
+ "templates.starter.v-wasm": {
+ "translation": "Стартер V"
+ },
"templates.starter.vb-wasm": {
"translation": "Стартер VB.NET (Wasm)"
},
diff --git a/src/livecodes/i18n/locales/ru/translation.ts b/src/livecodes/i18n/locales/ru/translation.ts
index 0073f09164..160e24e781 100644
--- a/src/livecodes/i18n/locales/ru/translation.ts
+++ b/src/livecodes/i18n/locales/ru/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'Стартер Tcl',
teal: 'Стартер Teal',
typescript: 'Стартер TypeScript',
+ 'v-wasm': 'Стартер V',
'vb-wasm': 'Стартер VB.NET (Wasm)',
vue: 'Стартер Vue',
vue2: 'Стартер Vue 2',
diff --git a/src/livecodes/i18n/locales/tr/language-info.lokalise.json b/src/livecodes/i18n/locales/tr/language-info.lokalise.json
index 935c311b93..954a17d49b 100644
--- a/src/livecodes/i18n/locales/tr/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/tr/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "WebAssembly üzerinde çalışan V derleyicisi."
+ },
+ "v-wasm.link": {
+ "translation": " V web sitesi V dil belgeleri Y dakikada X'i öğrenin, burada X=V LiveCodes Belgeleri Başlangıç şablonunu yükle "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "WebAssembly üzerinde çalışan VB.NET derleyicisi, Roslyn ve .NET WebAssembly çalışma zamanı kullanılarak."
},
diff --git a/src/livecodes/i18n/locales/tr/language-info.ts b/src/livecodes/i18n/locales/tr/language-info.ts
index aac52ba913..11148f60ae 100644
--- a/src/livecodes/i18n/locales/tr/language-info.ts
+++ b/src/livecodes/i18n/locales/tr/language-info.ts
@@ -515,6 +515,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>Resmi web sitesi2> 1> <3> <4>TypeScript dokümantasyonu4> 3> <5> <6>Y Dakikada X Öğrenin, X=TypeScript6> 5> <7> <8>Başlangıç şablonunu yükle8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'WebAssembly üzerinde çalışan V derleyicisi.',
+ link: "<1> <2>V web sitesi2> 1> <3> <4>V dil belgeleri4> 3> <5> <6>Y dakikada X'i öğrenin, burada X=V6> 5> <7> <8>LiveCodes Belgeleri8> 7> <9> <10>Başlangıç şablonunu yükle10> 9>",
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'WebAssembly üzerinde çalışan VB.NET derleyicisi, <1>Roslyn1> ve <2>.NET WebAssembly çalışma zamanı2> kullanılarak.',
link: '<1> <2>Visual Basic dil dokümantasyonu2> 1> <3> <4>Y Dakikada X Öğrenin, X=Visual Basic4> 3> <5> <6>LiveCodes Dokümantasyonu6> 5> <7> <8>Başlangıç şablonunu yükle8> 7>',
diff --git a/src/livecodes/i18n/locales/tr/translation.lokalise.json b/src/livecodes/i18n/locales/tr/translation.lokalise.json
index 9d8bcbdd5d..07e4d62715 100644
--- a/src/livecodes/i18n/locales/tr/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/tr/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript Başlangıç"
},
+ "templates.starter.v-wasm": {
+ "translation": "V Başlangıç"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (Wasm) Başlangıç"
},
diff --git a/src/livecodes/i18n/locales/tr/translation.ts b/src/livecodes/i18n/locales/tr/translation.ts
index 3cc5c451b0..8613a9e5d1 100644
--- a/src/livecodes/i18n/locales/tr/translation.ts
+++ b/src/livecodes/i18n/locales/tr/translation.ts
@@ -1060,6 +1060,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl Başlangıç',
teal: 'Teal Başlangıç',
typescript: 'TypeScript Başlangıç',
+ 'v-wasm': 'V Başlangıç',
'vb-wasm': 'VB.NET (Wasm) Başlangıç',
vue: 'Vue SFC Başlangıç',
vue2: 'Vue 2 Başlangıç',
diff --git a/src/livecodes/i18n/locales/ur/language-info.lokalise.json b/src/livecodes/i18n/locales/ur/language-info.lokalise.json
index 4c7705337a..e4edd1524e 100644
--- a/src/livecodes/i18n/locales/ur/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/ur/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "V کمپائلر WebAssembly پر چل رہا ہے۔"
+ },
+ "v-wasm.link": {
+ "translation": " V ویب سائٹ V زبان کی دستاویزات X=V — V کو چند منٹوں میں سیکھیں LiveCodes دستاویزات اسٹارٹر ٹیمپلیٹ لوڈ کریں "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "VB.NET کمپائلر ویب اسمبلی پر چل رہا ہے، Roslyn اور .NET WebAssembly رن ٹائم استعمال کر رہا ہے"
},
diff --git a/src/livecodes/i18n/locales/ur/language-info.ts b/src/livecodes/i18n/locales/ur/language-info.ts
index 84355149fa..542bb40624 100644
--- a/src/livecodes/i18n/locales/ur/language-info.ts
+++ b/src/livecodes/i18n/locales/ur/language-info.ts
@@ -517,6 +517,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>سرکاری ویب سائٹ2> 1> <3> <4>TypeScript دستاویزات4> 3> <5> <6>X کو Y منٹ میں سیکھیں، جہاں X=TypeScript6> 5> <7> <8>شروعاتی سانچہ لوڈ کریں8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: 'V کمپائلر WebAssembly پر چل رہا ہے۔',
+ link: '<1> <2>V ویب سائٹ2> 1> <3> <4>V زبان کی دستاویزات4> 3> <5> <6>X=V — V کو چند منٹوں میں سیکھیں6> 5> <7> <8>LiveCodes دستاویزات8> 7> <9> <10>اسٹارٹر ٹیمپلیٹ لوڈ کریں10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: 'VB.NET کمپائلر ویب اسمبلی پر چل رہا ہے، <1>Roslyn1> اور <2>.NET WebAssembly رن ٹائم2> استعمال کر رہا ہے',
link: '<1> <2> Visual Basic زبان کی دستاویزات 2> 1> <3> <4> Y منٹوں میں X سیکھیں، جہاں X=Visual Basic 4> 3> <5> <6> LiveCodes ڈاکومینٹیشن 6> 5> <7> <8> اسٹارٹر ٹیمپلیٹ لوڈ کریں 8> 7>',
diff --git a/src/livecodes/i18n/locales/ur/translation.lokalise.json b/src/livecodes/i18n/locales/ur/translation.lokalise.json
index 6abab68d77..f6c5b0d2c5 100644
--- a/src/livecodes/i18n/locales/ur/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/ur/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "ٹائپ اسکرپٹ شروعاتی"
},
+ "templates.starter.v-wasm": {
+ "translation": "V شروعاتی"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET (واسم) شروعاتی"
},
diff --git a/src/livecodes/i18n/locales/ur/translation.ts b/src/livecodes/i18n/locales/ur/translation.ts
index 592a0d7d93..7231548fc8 100644
--- a/src/livecodes/i18n/locales/ur/translation.ts
+++ b/src/livecodes/i18n/locales/ur/translation.ts
@@ -1059,6 +1059,7 @@ const translation: I18nTranslation = {
tcl: 'ٹی سی ایل شروعاتی',
teal: 'ٹیل شروعاتی',
typescript: 'ٹائپ اسکرپٹ شروعاتی',
+ 'v-wasm': 'V شروعاتی',
'vb-wasm': 'VB.NET (واسم) شروعاتی',
vue: 'ویو شروعاتی',
vue2: 'ویو 2 شروعاتی',
diff --git a/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json b/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json
index cb11cf9eb3..7441fdf48a 100644
--- a/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json
+++ b/src/livecodes/i18n/locales/zh-CN/language-info.lokalise.json
@@ -912,6 +912,15 @@
"typescript.name": {
"translation": "TypeScript"
},
+ "v-wasm.desc": {
+ "translation": "在 WebAssembly 上运行的 V 编译器。"
+ },
+ "v-wasm.link": {
+ "translation": " V 网站 V 语言文档 Y分钟掌握X,其中X=V LiveCodes 文档 加载入门模板 "
+ },
+ "v-wasm.name": {
+ "translation": "V (Wasm)"
+ },
"vbWasm.desc": {
"translation": "在 WebAssembly 上运行的 VB.NET 编译器,使用 Roslyn 和 .NET WebAssembly 运行时"
},
diff --git a/src/livecodes/i18n/locales/zh-CN/language-info.ts b/src/livecodes/i18n/locales/zh-CN/language-info.ts
index 400ddd621a..8f1b743c51 100644
--- a/src/livecodes/i18n/locales/zh-CN/language-info.ts
+++ b/src/livecodes/i18n/locales/zh-CN/language-info.ts
@@ -508,6 +508,11 @@ const languageInfo: I18nLangInfoTranslation = {
link: '<1> <2>官方网站2> 1> <3> <4>TypeScript 文档4> 3> <5> <6>在 Y 分钟内学习 X,其中 X=TypeScript6> 5> <7> <8>加载启动模板8> 7>',
name: 'TypeScript',
},
+ 'v-wasm': {
+ desc: '在 WebAssembly 上运行的 V 编译器。',
+ link: '<1> <2>V 网站2> 1> <3> <4>V 语言文档4> 3> <5> <6>Y分钟掌握X,其中X=V6> 5> <7> <8>LiveCodes 文档8> 7> <9> <10>加载入门模板10> 9>',
+ name: 'V (Wasm)',
+ },
vbWasm: {
desc: '在 WebAssembly 上运行的 VB.NET 编译器,使用 <1>Roslyn1> 和 <2>.NET WebAssembly 运行时2>',
link: '<1> <2> Visual Basic 语言文档2> 1> <3> <4>在 Y 分钟内学习 X,其中 X=Visual Basic 4> 3> <5> <6> LiveCodes 文档6> 5> <7> <8>加载入门模板8> 7>',
diff --git a/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json b/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json
index 360d3eb3dc..e24af0a22a 100644
--- a/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json
+++ b/src/livecodes/i18n/locales/zh-CN/translation.lokalise.json
@@ -2160,6 +2160,9 @@
"templates.starter.typescript": {
"translation": "TypeScript 启动模板"
},
+ "templates.starter.v-wasm": {
+ "translation": "V 启动模板"
+ },
"templates.starter.vb-wasm": {
"translation": "VB.NET(WASM)启动模板"
},
diff --git a/src/livecodes/i18n/locales/zh-CN/translation.ts b/src/livecodes/i18n/locales/zh-CN/translation.ts
index 759aa9540b..02b0401d48 100644
--- a/src/livecodes/i18n/locales/zh-CN/translation.ts
+++ b/src/livecodes/i18n/locales/zh-CN/translation.ts
@@ -1057,6 +1057,7 @@ const translation: I18nTranslation = {
tcl: 'Tcl 启动模板',
teal: 'Teal 启动模板',
typescript: 'TypeScript 启动模板',
+ 'v-wasm': 'V 启动模板',
'vb-wasm': 'VB.NET(WASM)启动模板',
vue: 'Vue 单文件组件启动模板',
vue2: 'Vue 2 启动模板',
diff --git a/src/livecodes/languages/languages.ts b/src/livecodes/languages/languages.ts
index 47e4e63a1e..105e5f7fa7 100644
--- a/src/livecodes/languages/languages.ts
+++ b/src/livecodes/languages/languages.ts
@@ -90,6 +90,7 @@ import { tcl } from './tcl';
import { teal } from './teal';
import { twig } from './twig';
import { typescript } from './typescript';
+import { vWasm } from './v-wasm';
import { vbWasm } from './vb-wasm';
import { vento } from './vento';
import { vue, vueApp } from './vue';
@@ -177,6 +178,7 @@ export const languages: LanguageSpecs[] = [
java,
csharpWasm,
blazorWasm,
+ vWasm,
vbWasm,
fsharp,
fsharpWasm,
diff --git a/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts b/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts
new file mode 100644
index 0000000000..c89cfb163b
--- /dev/null
+++ b/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts
@@ -0,0 +1,32 @@
+import { vWasm } from '../lang-v-wasm';
+
+describe('V (Wasm) language spec', () => {
+ test('is registered as v-wasm, with the wasm compiler script', () => {
+ expect(vWasm.name).toBe('v-wasm');
+ expect(vWasm.title).toBe('V (Wasm)');
+ expect(vWasm.compiler).toMatchObject({
+ scriptType: 'text/v-wasm',
+ compiledCodeLanguage: 'v',
+ liveReload: true,
+ });
+ });
+
+ test('accepts the common V extensions', () => {
+ expect(vWasm.extensions).toEqual(
+ expect.arrayContaining(['v', 'vlang', 'v-wasm', 'wasm.v', 'vsh']),
+ );
+ });
+
+ test('warns that the first run is a large download', () => {
+ // The V compiler and the Clang toolchain together are tens of megabytes.
+ expect(vWasm.largeDownload).toBe(true);
+ });
+
+ test('uses a code editor with V support', () => {
+ expect(vWasm.editor).toBe('script');
+ // There is no dedicated V mode, so the Go mode stands in for the editors that lack one.
+ expect(vWasm.editorSupport?.monaco?.language).toBe('go');
+ // Prism does ship a V grammar, so CodeJar uses the real one.
+ expect(vWasm.editorSupport?.codejar?.language).toBe('v');
+ });
+});
diff --git a/src/livecodes/languages/v-wasm/index.ts b/src/livecodes/languages/v-wasm/index.ts
new file mode 100644
index 0000000000..33584b8e7c
--- /dev/null
+++ b/src/livecodes/languages/v-wasm/index.ts
@@ -0,0 +1 @@
+export * from './lang-v-wasm';
diff --git a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
new file mode 100644
index 0000000000..20e5072a87
--- /dev/null
+++ b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
@@ -0,0 +1,329 @@
+/* eslint-disable no-console */
+import { createWorkerFromContent, getErrorMessage } from '../../utils/utils';
+import { vWasmBaseUrl, vWasmClangBaseUrl } from '../../vendors';
+
+// One runner for one language. The compiler is @live-codes/v-wasm, loaded from its CDN build: the
+// IIFE defines `self.vWasm`, which the worker reaches with importScripts. It compiles V to C and
+// hands that to the same Clang toolchain the C/C++ languages use, then runs the linked module in
+// place, because a browser has no linker subprocess to hand a binary to.
+//
+// The V compiler is a classic Emscripten bundle: it decides it is in a worker by finding
+// `importScripts`, so it has to run in a classic worker (the one createWorkerFromContent builds).
+const SCRIPT_TYPE = 'text/v-wasm';
+
+interface WorkerRunResult {
+ output: string;
+ errors: string[];
+ exitCode: number | null;
+}
+
+interface RunResult {
+ input: string;
+ output: string | null;
+ error: string | null;
+ exitCode: number | null;
+}
+
+interface Runner {
+ ensureReady: () => Promise;
+ run: (code: string, input: string) => Promise;
+}
+
+interface VWasmApi {
+ ready: boolean;
+ failed: boolean;
+ input: string;
+ output: string | null;
+ error: string | null;
+ exitCode: number | null;
+ loaded: Promise;
+ init: Promise | null;
+ runner: Runner | null;
+ run: (input?: string) => Promise;
+}
+
+declare const window: Window & {
+ livecodes: Record;
+};
+
+const getWorkerSrc = () => `
+importScripts(${JSON.stringify(vWasmBaseUrl + 'dist/v-wasm.global.js')});
+
+let compiler = null;
+
+const getCompiler = () => {
+ compiler =
+ compiler ||
+ self.vWasm.createCompiler({
+ baseUrl: ${JSON.stringify(vWasmBaseUrl + 'assets/v/')},
+ // The Clang half is pinned by the package, so it comes from that exact version's assets.
+ clangBaseUrl: ${JSON.stringify(vWasmClangBaseUrl)},
+ });
+ return compiler;
+};
+
+addEventListener('message', async (event) => {
+ const { id, code, input } = event.data;
+ try {
+ const result = await (await getCompiler()).run(code, input);
+ postMessage({
+ id,
+ result: {
+ output: result.output,
+ // The package reports V's, clang's and the linker's diagnostics as a list of lines, empty
+ // when the program built and ran.
+ errors: result.errors || [],
+ exitCode: result.exitCode,
+ },
+ });
+ } catch (error) {
+ postMessage({ id, error: String((error && error.message) || error) });
+ }
+});
+
+getCompiler().then(
+ () => postMessage({ type: 'ready' }),
+ (error) => postMessage({ type: 'error', message: String((error && error.message) || error) }),
+);
+`;
+
+// A single compile + run is normally quick; a long timeout guards against hangs (e.g. an infinite
+// loop in the user program) by killing and respawning the worker.
+const RUN_TIMEOUT_MS = 120000;
+
+interface Pending {
+ resolve: (result: WorkerRunResult) => void;
+ reject: (error: Error) => void;
+ timer: ReturnType;
+}
+
+const createRunner = (): Runner => {
+ let worker: Worker | null = null;
+ let ready: Promise | null = null;
+ let settleReady: ((error?: Error) => void) | null = null;
+ let pending: Record = {};
+ let nextId = 1;
+
+ const failAll = (error: Error) => {
+ for (const id of Object.keys(pending)) {
+ const request = pending[Number(id)];
+ clearTimeout(request.timer);
+ request.reject(error);
+ }
+ pending = {};
+ };
+
+ const teardown = (error: Error) => {
+ worker?.terminate();
+ worker = null;
+ ready = null;
+ settleReady?.(error);
+ settleReady = null;
+ failAll(error);
+ };
+
+ const onMessage = (event: MessageEvent) => {
+ const message = event.data ?? {};
+ if (message.type === 'ready') {
+ settleReady?.();
+ settleReady = null;
+ return;
+ }
+ if (message.type === 'error') {
+ // The runtime failed to load (network, or the browser is unsupported).
+ teardown(new Error(message.message));
+ return;
+ }
+ const request = pending[message.id];
+ if (!request) return;
+ delete pending[message.id];
+ clearTimeout(request.timer);
+ if (message.error != null) {
+ request.reject(new Error(message.error));
+ } else {
+ request.resolve(message.result);
+ }
+ };
+
+ const spawn = () => {
+ ready = new Promise((resolve, reject) => {
+ settleReady = (error?: Error) => (error ? reject(error) : resolve());
+ });
+ worker = createWorkerFromContent(getWorkerSrc());
+ worker.onmessage = onMessage;
+ worker.onerror = (event) => teardown(new Error(`The V worker crashed: ${event.message}`));
+ };
+
+ /** Spawn the worker if needed and resolve once the compiler has loaded. */
+ const ensureReady = async () => {
+ if (!ready) spawn();
+ await ready;
+ };
+
+ const run = (code: string, input: string) =>
+ ensureReady().then(
+ () =>
+ new Promise((resolve, reject) => {
+ // `ensureReady` can resolve after the worker has been torn down (crash or timeout), so
+ // the worker may be gone by the time the request is registered.
+ if (!worker) {
+ reject(new Error('The V worker is not available'));
+ return;
+ }
+ const id = nextId++;
+ const timer = setTimeout(() => {
+ // The worker hung — kill it and reject, so the next run respawns a fresh worker.
+ teardown(new Error('The V compiler timed out'));
+ }, RUN_TIMEOUT_MS);
+ pending[id] = { resolve, reject, timer };
+ worker.postMessage({ id, code, input });
+ }),
+ );
+
+ return { ensureReady, run };
+};
+
+/**
+ * Reads the program out of the result page.
+ *
+ * The result page writes the code inline as the `innerHTML` of a
+ * `
+`.trimStart(),
+ },
+ style: {
+ language: 'css',
+ content: `
+.container,
+.container button {
+ text-align: center;
+ font: 1em sans-serif;
+}
+.logo {
+ width: 150px;
+}
+`.trimStart(),
+ },
+ script: {
+ language: 'v-wasm',
+ content: `
+import os
+
+fn main() {
+ count := os.input('').int()
+ println('V')
+ println(count + 1)
+}
+`.trimStart(),
+ },
+};
diff --git a/src/livecodes/vendors.ts b/src/livecodes/vendors.ts
index 8fd81b246f..d39e0f7865 100644
--- a/src/livecodes/vendors.ts
+++ b/src/livecodes/vendors.ts
@@ -531,6 +531,12 @@ export const unicornJsX86Url = /* @__PURE__ */ getUrl(
export const uniterUrl = /* @__PURE__ */ getUrl('uniter@2.18.0/dist/uniter.js');
+// `@live-codes/v-wasm` bundles this exact `@live-codes/clang-wasm` version for the Clang half of its
+// pipeline, so its assets are fetched from that version rather than the one the C/C++ languages use.
+export const vWasmClangBaseUrl = /* @__PURE__ */ getUrl('@live-codes/clang-wasm@0.3.0/assets/');
+
+export const vWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/v-wasm@0.1.0/');
+
export const vbWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/vb-wasm@0.1.1/');
export const vegaCdnUrl = /* @__PURE__ */ getUrl('vega@5.25.0/build/vega.js');
diff --git a/src/sdk/models.ts b/src/sdk/models.ts
index b746207a07..67efd59750 100644
--- a/src/sdk/models.ts
+++ b/src/sdk/models.ts
@@ -185,6 +185,11 @@ export type Language =
| 'ftn'
| 'fortran-wasm'
| 'lfortran'
+ | 'v'
+ | 'vlang'
+ | 'v-wasm'
+ | 'wasm.v'
+ | 'vsh'
| 'cpp-wasm'
| 'cppwasm'
| 'wasm.cpp'
@@ -484,6 +489,7 @@ export type TemplateName =
| 'cpp-wasm'
| 'objc-wasm'
| 'fortran-wasm'
+ | 'v-wasm'
| 'java'
| 'csharp-wasm'
| 'blazor-wasm'
@@ -911,6 +917,7 @@ export type CustomSettings = Partial<
| 'text/minizinc'
| 'text/go-wasm'
| 'text/zig-wasm'
+ | 'text/v-wasm'
| 'application/json'
| 'application/lua'
| 'text/fennel'
diff --git a/storybook/_stories/EmbedOptions/template.ts b/storybook/_stories/EmbedOptions/template.ts
index d9ff83d65c..a64a7f0c72 100644
--- a/storybook/_stories/EmbedOptions/template.ts
+++ b/storybook/_stories/EmbedOptions/template.ts
@@ -51,6 +51,7 @@ const storyDef: StoryDef = {
CWasm: { props: { template: 'c-wasm' } },
CppWasm: { props: { template: 'cpp-wasm' } },
ObjcWasm: { props: { template: 'objc-wasm' } },
+ VWasm: { props: { template: 'v-wasm' } },
RustWasm: { props: { template: 'rust-wasm' } },
SwiftWasm: { props: { template: 'swift-wasm' } },
ZigWasm: { props: { template: 'zig-wasm' } },
diff --git a/storybook/preact/stories/EmbedOptions/template.stories.ts b/storybook/preact/stories/EmbedOptions/template.stories.ts
index a9e2264a5f..c775c91586 100644
--- a/storybook/preact/stories/EmbedOptions/template.stories.ts
+++ b/storybook/preact/stories/EmbedOptions/template.stories.ts
@@ -66,6 +66,7 @@ export const CWasm = livecodesStory({ template: 'c-wasm' });
export const CppWasm = livecodesStory({ template: 'cpp-wasm' });
export const ObjcWasm = livecodesStory({ template: 'objc-wasm' });
export const FortranWasm = livecodesStory({ template: 'fortran-wasm' });
+export const VWasm = livecodesStory({ template: 'v-wasm' });
export const RustWasm = livecodesStory({ template: 'rust-wasm' });
export const SwiftWasm = livecodesStory({ template: 'swift-wasm' });
export const ZigWasm = livecodesStory({ template: 'zig-wasm' });
diff --git a/storybook/react/stories/EmbedOptions/template.stories.ts b/storybook/react/stories/EmbedOptions/template.stories.ts
index a9e2264a5f..c775c91586 100644
--- a/storybook/react/stories/EmbedOptions/template.stories.ts
+++ b/storybook/react/stories/EmbedOptions/template.stories.ts
@@ -66,6 +66,7 @@ export const CWasm = livecodesStory({ template: 'c-wasm' });
export const CppWasm = livecodesStory({ template: 'cpp-wasm' });
export const ObjcWasm = livecodesStory({ template: 'objc-wasm' });
export const FortranWasm = livecodesStory({ template: 'fortran-wasm' });
+export const VWasm = livecodesStory({ template: 'v-wasm' });
export const RustWasm = livecodesStory({ template: 'rust-wasm' });
export const SwiftWasm = livecodesStory({ template: 'swift-wasm' });
export const ZigWasm = livecodesStory({ template: 'zig-wasm' });
diff --git a/storybook/solid/stories/EmbedOptions/template.stories.ts b/storybook/solid/stories/EmbedOptions/template.stories.ts
index a9e2264a5f..c775c91586 100644
--- a/storybook/solid/stories/EmbedOptions/template.stories.ts
+++ b/storybook/solid/stories/EmbedOptions/template.stories.ts
@@ -66,6 +66,7 @@ export const CWasm = livecodesStory({ template: 'c-wasm' });
export const CppWasm = livecodesStory({ template: 'cpp-wasm' });
export const ObjcWasm = livecodesStory({ template: 'objc-wasm' });
export const FortranWasm = livecodesStory({ template: 'fortran-wasm' });
+export const VWasm = livecodesStory({ template: 'v-wasm' });
export const RustWasm = livecodesStory({ template: 'rust-wasm' });
export const SwiftWasm = livecodesStory({ template: 'swift-wasm' });
export const ZigWasm = livecodesStory({ template: 'zig-wasm' });
diff --git a/storybook/svelte/stories/EmbedOptions/template.stories.ts b/storybook/svelte/stories/EmbedOptions/template.stories.ts
index a9e2264a5f..c775c91586 100644
--- a/storybook/svelte/stories/EmbedOptions/template.stories.ts
+++ b/storybook/svelte/stories/EmbedOptions/template.stories.ts
@@ -66,6 +66,7 @@ export const CWasm = livecodesStory({ template: 'c-wasm' });
export const CppWasm = livecodesStory({ template: 'cpp-wasm' });
export const ObjcWasm = livecodesStory({ template: 'objc-wasm' });
export const FortranWasm = livecodesStory({ template: 'fortran-wasm' });
+export const VWasm = livecodesStory({ template: 'v-wasm' });
export const RustWasm = livecodesStory({ template: 'rust-wasm' });
export const SwiftWasm = livecodesStory({ template: 'swift-wasm' });
export const ZigWasm = livecodesStory({ template: 'zig-wasm' });
diff --git a/storybook/vue/stories/EmbedOptions/template.stories.ts b/storybook/vue/stories/EmbedOptions/template.stories.ts
index a9e2264a5f..c775c91586 100644
--- a/storybook/vue/stories/EmbedOptions/template.stories.ts
+++ b/storybook/vue/stories/EmbedOptions/template.stories.ts
@@ -66,6 +66,7 @@ export const CWasm = livecodesStory({ template: 'c-wasm' });
export const CppWasm = livecodesStory({ template: 'cpp-wasm' });
export const ObjcWasm = livecodesStory({ template: 'objc-wasm' });
export const FortranWasm = livecodesStory({ template: 'fortran-wasm' });
+export const VWasm = livecodesStory({ template: 'v-wasm' });
export const RustWasm = livecodesStory({ template: 'rust-wasm' });
export const SwiftWasm = livecodesStory({ template: 'swift-wasm' });
export const ZigWasm = livecodesStory({ template: 'zig-wasm' });
diff --git a/storybook/web-components/stories/EmbedOptions/template.stories.ts b/storybook/web-components/stories/EmbedOptions/template.stories.ts
index a9e2264a5f..c775c91586 100644
--- a/storybook/web-components/stories/EmbedOptions/template.stories.ts
+++ b/storybook/web-components/stories/EmbedOptions/template.stories.ts
@@ -66,6 +66,7 @@ export const CWasm = livecodesStory({ template: 'c-wasm' });
export const CppWasm = livecodesStory({ template: 'cpp-wasm' });
export const ObjcWasm = livecodesStory({ template: 'objc-wasm' });
export const FortranWasm = livecodesStory({ template: 'fortran-wasm' });
+export const VWasm = livecodesStory({ template: 'v-wasm' });
export const RustWasm = livecodesStory({ template: 'rust-wasm' });
export const SwiftWasm = livecodesStory({ template: 'swift-wasm' });
export const ZigWasm = livecodesStory({ template: 'zig-wasm' });
diff --git a/vendor-licenses.md b/vendor-licenses.md
index 1df1547e4a..42277e8879 100644
--- a/vendor-licenses.md
+++ b/vendor-licenses.md
@@ -348,6 +348,10 @@ wacl: [BSD 3-Clause License](https://github.com/ecky-l/wacl/blob/9daacabb0102a99
LFortran (Wasm): [BSD 3-Clause License](https://github.com/lfortran/lfortran/blob/main/LICENSE)
+@live-codes/v-wasm: [MIT License](https://github.com/live-codes/browser-v/blob/main/LICENSE)
+
+V (Wasm): [MIT License](https://github.com/vlang/v/blob/master/LICENSE) (the V compiler and its standard library, compiled to WebAssembly by @live-codes/v-wasm; the Clang toolchain it links through is covered by LLVM / LLD below)
+
LLVM / LLD (Wasm): [Apache License 2.0 with LLVM-exception](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT)
@wasm-fmt/clang-format: [MIT License](https://github.com/wasm-fmt/clang-format/blob/6a84f2d980e5ff4145bd9178a49cd5146e48df78/LICENSE)
From 7998504847701e93761bed1e7989826b6ced79e2 Mon Sep 17 00:00:00 2001
From: "pullfrog[bot]" <226033991+pullfrog[bot]@users.noreply.github.com>
Date: Tue, 6 Oct 2026 09:29:09 +0000
Subject: [PATCH 2/5] fix: deduplicate WASM worker runner to satisfy SonarCloud
gate
The quality gate failed on 16.6% duplication on new code. Extract the worker runner shared by the WASM languages into worker-runner.ts and use it from the v-wasm, fortran, clang, ada and assembly scripts, replacing the copy in each.
---
.../ada-wasm/lang-ada-wasm-script.ts | 93 +------------
.../lang-assembly-wasm-script.ts | 97 +------------
.../clang-wasm/lang-clang-wasm-script.ts | 95 +------------
.../fortran-wasm/lang-fortran-wasm-script.ts | 110 +--------------
.../languages/v-wasm/lang-v-wasm-script.ts | 109 +--------------
src/livecodes/languages/worker-runner.ts | 130 ++++++++++++++++++
6 files changed, 153 insertions(+), 481 deletions(-)
create mode 100644 src/livecodes/languages/worker-runner.ts
diff --git a/src/livecodes/languages/ada-wasm/lang-ada-wasm-script.ts b/src/livecodes/languages/ada-wasm/lang-ada-wasm-script.ts
index 58d7190c31..0d814c1d16 100644
--- a/src/livecodes/languages/ada-wasm/lang-ada-wasm-script.ts
+++ b/src/livecodes/languages/ada-wasm/lang-ada-wasm-script.ts
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
-import { createWorkerFromContent, getErrorMessage } from '../../utils/utils';
+import { getErrorMessage } from '../../utils/utils';
import { adaWasmBaseUrl } from '../../vendors';
+import { createWorkerRunner, type Runner } from '../worker-runner';
// Ada is compiled and run by HAC (@live-codes/ada-wasm), a single self-contained
// WebAssembly bundle. It runs inside a dedicated worker so a non-terminating
@@ -24,12 +25,6 @@ interface RunResult {
exitCode: number | null;
}
-interface Runner {
- ensureReady: () => Promise;
- run: (code: string, input: string) => Promise;
- destroy: () => void;
-}
-
interface AdaWasmApi {
ready: boolean;
failed: boolean;
@@ -39,7 +34,7 @@ interface AdaWasmApi {
exitCode: number | null;
loaded: Promise;
init: Promise | null;
- runner?: Runner;
+ runner?: Runner;
run: (input?: string) => Promise;
}
@@ -103,83 +98,6 @@ getRuntime().then(
);
`;
-interface Pending {
- resolve: (result: WorkerRunResult) => void;
- reject: (error: Error) => void;
-}
-
-const createRunner = (): Runner => {
- let worker: Worker | null = null;
- let ready: Promise | null = null;
- let settleReady: ((error?: Error) => void) | null = null;
- let pending: Record = {};
- let nextId = 1;
-
- const failAll = (error: Error) => {
- for (const id of Object.keys(pending)) {
- pending[Number(id)].reject(error);
- }
- pending = {};
- };
-
- const teardown = (error?: Error) => {
- worker?.terminate();
- worker = null;
- ready = null;
- settleReady?.(error);
- settleReady = null;
- if (error) failAll(error);
- };
-
- const onMessage = (event: MessageEvent) => {
- const message = event.data ?? {};
- if (message.type === 'ready') {
- settleReady?.();
- settleReady = null;
- return;
- }
- if (message.type === 'error') {
- // The runtime failed to load (network, or the browser is unsupported).
- teardown(new Error(message.message));
- return;
- }
- const request = pending[message.id];
- if (!request) return;
- delete pending[message.id];
- if (message.error != null) {
- request.reject(new Error(message.error));
- } else {
- request.resolve(message.result);
- }
- };
-
- const spawn = () => {
- ready = new Promise((resolve, reject) => {
- settleReady = (error?: Error) => (error ? reject(error) : resolve());
- });
- worker = createWorkerFromContent(getWorkerSrc());
- worker.onmessage = onMessage;
- worker.onerror = (event) => teardown(new Error(`The Ada worker crashed: ${event.message}`));
- };
-
- const ensureReady = async () => {
- if (!ready) spawn();
- await ready;
- };
-
- const run = (code: string, input: string) =>
- ensureReady().then(
- () =>
- new Promise((resolve, reject) => {
- const id = nextId++;
- pending[id] = { resolve, reject };
- worker?.postMessage({ id, code, input });
- }),
- );
-
- return { ensureReady, run, destroy: () => teardown() };
-};
-
const setResult = (
input: string,
output: string | null,
@@ -207,7 +125,7 @@ adaWasm.ready = false;
adaWasm.failed = false;
/** Start (once) loading the runtime in a worker, showing the loading indicator. */
-const ensureLoaded = (runner: Runner): Promise => {
+const ensureLoaded = (runner: Runner): Promise => {
let init = adaWasm.init;
if (!init) {
adaWasm.failed = false;
@@ -258,7 +176,8 @@ adaWasm.run = async (input?: string) => {
});
if (!code.trim()) return setResult(stdin, null, null, 0);
- const runner = (adaWasm.runner = adaWasm.runner || createRunner());
+ const runner = (adaWasm.runner =
+ adaWasm.runner || createWorkerRunner({ getWorkerSrc, label: 'Ada' }));
try {
await ensureLoaded(runner);
diff --git a/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts b/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts
index 0b1d063490..b2b031da86 100644
--- a/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts
+++ b/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts
@@ -1,11 +1,12 @@
/* eslint-disable no-console */
-import { createWorkerFromContent, getErrorMessage } from '../../utils/utils';
+import { getErrorMessage } from '../../utils/utils';
import {
assemblyWasmBaseUrl,
keystoneJsUrl,
keystoneJsWasmUrl,
unicornJsX86Url,
} from '../../vendors';
+import { createWorkerRunner, type Runner } from '../worker-runner';
// x86-64 assembly is assembled and executed by @live-codes/assembly-wasm, which loads
// Keystone (the assembler) and Unicorn (the CPU emulator) as separate WebAssembly
@@ -20,12 +21,6 @@ const SCRIPT_TYPE = 'text/assembly';
const BOOT_TIMEOUT_MS = 2 * 60_000;
-interface WorkerRunResult {
- output: string;
- errors: string[];
- exitCode: number | null;
-}
-
interface RunResult {
input: string;
output: string | null;
@@ -33,12 +28,6 @@ interface RunResult {
exitCode: number | null;
}
-interface Runner {
- ensureReady: () => Promise;
- run: (code: string, input: string) => Promise;
- destroy: () => void;
-}
-
interface AssemblyWasmApi {
ready: boolean;
failed: boolean;
@@ -118,85 +107,6 @@ getCompiler().then(
);
`;
-interface Pending {
- resolve: (result: WorkerRunResult) => void;
- reject: (error: Error) => void;
-}
-
-const createRunner = (): Runner => {
- let worker: Worker | null = null;
- let ready: Promise | null = null;
- let settleReady: ((error?: Error) => void) | null = null;
- let pending: Record = {};
- let nextId = 1;
-
- const failAll = (error: Error) => {
- for (const id of Object.keys(pending)) {
- pending[Number(id)].reject(error);
- }
- pending = {};
- };
-
- const teardown = (error?: Error) => {
- worker?.terminate();
- worker = null;
- ready = null;
- settleReady?.(error);
- settleReady = null;
- if (error) failAll(error);
- };
-
- const onMessage = (event: MessageEvent) => {
- const message = event.data ?? {};
- if (message.type === 'ready') {
- settleReady?.();
- settleReady = null;
- return;
- }
- if (message.type === 'error') {
- // The runtime failed to load (network, or the browser is unsupported).
- teardown(new Error(message.message));
- return;
- }
- const request = pending[message.id];
- if (!request) return;
- delete pending[message.id];
- if (message.error != null) {
- request.reject(new Error(message.error));
- } else {
- request.resolve(message.result);
- }
- };
-
- const spawn = () => {
- ready = new Promise((resolve, reject) => {
- settleReady = (error?: Error) => (error ? reject(error) : resolve());
- });
- worker = createWorkerFromContent(getWorkerSrc());
- worker.onmessage = onMessage;
- worker.onerror = (event) =>
- teardown(new Error(`The Assembly worker crashed: ${event.message}`));
- };
-
- /** Spawn the worker if needed and resolve once the runtime has loaded. */
- const ensureReady = async () => {
- if (!ready) spawn();
- await ready;
- };
-
- const run = (code: string, input: string) =>
- ensureReady().then(
- () =>
- new Promise((resolve, reject) => {
- const id = nextId++;
- pending[id] = { resolve, reject };
- worker?.postMessage({ id, code, input });
- }),
- );
-
- return { ensureReady, run, destroy: () => teardown() };
-};
-
const setResult = (
input: string,
output: string | null,
@@ -277,7 +187,8 @@ assemblyWasm.run = async (input?: string) => {
// Park the runner on the persisted namespace so a live reload reuses the warm
// worker instead of loading the runtimes again.
- const runner = (assemblyWasm.runner = assemblyWasm.runner || createRunner());
+ const runner = (assemblyWasm.runner =
+ assemblyWasm.runner || createWorkerRunner({ getWorkerSrc, label: 'Assembly' }));
try {
await ensureLoaded(runner);
diff --git a/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts b/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts
index 4b751753a9..6d53ab349b 100644
--- a/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts
+++ b/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
-import { createWorkerFromContent, getErrorMessage } from '../../utils/utils';
+import { getErrorMessage } from '../../utils/utils';
import { clangWasmBaseUrl } from '../../vendors';
+import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner serves all four Clang languages. The language is read from the script tags the
// compiler injected into the page, so only the script type differs between them.
@@ -35,12 +36,6 @@ interface ClangWasmSettings {
args?: string[];
}
-interface WorkerRunResult {
- output: string;
- errors: string[];
- exitCode: number | null;
-}
-
interface RunResult {
input: string;
output: string | null;
@@ -48,11 +43,6 @@ interface RunResult {
exitCode: number | null;
}
-interface Runner {
- ensureReady: () => Promise;
- run: (code: string, input: string, options?: ClangWasmSettings) => Promise;
-}
-
interface ClangWasmApi {
ready: boolean;
input: string;
@@ -107,84 +97,6 @@ getCompiler().then(
);
`;
-interface Pending {
- resolve: (result: WorkerRunResult) => void;
- reject: (error: Error) => void;
-}
-
-const createRunner = (language: LanguageId): Runner => {
- let worker: Worker | null = null;
- let ready: Promise | null = null;
- let settleReady: ((error?: Error) => void) | null = null;
- let pending: Record = {};
- let nextId = 1;
-
- const failAll = (error: Error) => {
- for (const id of Object.keys(pending)) {
- pending[Number(id)].reject(error);
- }
- pending = {};
- };
-
- const teardown = (error: Error) => {
- worker?.terminate();
- worker = null;
- ready = null;
- settleReady?.(error);
- settleReady = null;
- failAll(error);
- };
-
- const onMessage = (event: MessageEvent) => {
- const message = event.data ?? {};
- if (message.type === 'ready') {
- settleReady?.();
- settleReady = null;
- return;
- }
- if (message.type === 'error') {
- // The runtime failed to load (network, or the browser is unsupported).
- teardown(new Error(message.message));
- return;
- }
- const request = pending[message.id];
- if (!request) return;
- delete pending[message.id];
- if (message.error != null) {
- request.reject(new Error(message.error));
- } else {
- request.resolve(message.result);
- }
- };
-
- const spawn = () => {
- ready = new Promise((resolve, reject) => {
- settleReady = (error?: Error) => (error ? reject(error) : resolve());
- });
- worker = createWorkerFromContent(getWorkerSrc(language));
- worker.onmessage = onMessage;
- worker.onerror = (event) => teardown(new Error(`The Clang worker crashed: ${event.message}`));
- };
-
- /** Spawn the worker if needed and resolve once the compiler has loaded. */
- const ensureReady = async () => {
- if (!ready) spawn();
- await ready;
- };
-
- const run = (code: string, input: string, options?: ClangWasmSettings) =>
- ensureReady().then(
- () =>
- new Promise((resolve, reject) => {
- const id = nextId++;
- pending[id] = { resolve, reject };
- worker?.postMessage({ id, code, input, options });
- }),
- );
-
- return { ensureReady, run };
-};
-
const getLanguage = (): LanguageId | null => {
for (const scriptType of SCRIPT_TYPES) {
if (document.querySelector(`script[type="${scriptType}"]`)) {
@@ -282,7 +194,8 @@ clangWasm.run = async (input?: string) => {
if (!code.trim()) return setResult(stdin, null, null, null);
const runner = (clangWasm.runners[language] =
- clangWasm.runners[language] || createRunner(language));
+ clangWasm.runners[language] ||
+ createWorkerRunner({ getWorkerSrc: () => getWorkerSrc(language), label: 'Clang' }));
try {
await ensureLoaded(runner);
diff --git a/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts b/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts
index 4b7f702c63..5ba0b8e61c 100644
--- a/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts
+++ b/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
-import { createWorkerFromContent, getErrorMessage } from '../../utils/utils';
+import { getErrorMessage } from '../../utils/utils';
import { fortranWasmBaseUrl } from '../../vendors';
+import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. The compiler itself is @live-codes/lfortran-wasm, loaded from its
// CDN build: the IIFE defines `self.lfortranWasm`, which the worker reaches with importScripts.
@@ -8,12 +9,6 @@ import { fortranWasmBaseUrl } from '../../vendors';
// subprocess to hand a binary to.
const SCRIPT_TYPE = 'text/fortran-wasm';
-interface WorkerRunResult {
- output: string;
- errors: string[];
- exitCode: number | null;
-}
-
interface RunResult {
input: string;
output: string | null;
@@ -21,11 +16,6 @@ interface RunResult {
exitCode: number | null;
}
-interface Runner {
- ensureReady: () => Promise;
- run: (code: string, input: string) => Promise;
-}
-
interface FortranWasmApi {
ready: boolean;
input: string;
@@ -84,98 +74,6 @@ getCompiler().then(
// loop in the user program) by killing and respawning the worker.
const RUN_TIMEOUT_MS = 120000;
-interface Pending {
- resolve: (result: WorkerRunResult) => void;
- reject: (error: Error) => void;
- timer: ReturnType;
-}
-
-const createRunner = (): Runner => {
- let worker: Worker | null = null;
- let ready: Promise | null = null;
- let settleReady: ((error?: Error) => void) | null = null;
- let pending: Record = {};
- let nextId = 1;
-
- const failAll = (error: Error) => {
- for (const id of Object.keys(pending)) {
- const request = pending[Number(id)];
- clearTimeout(request.timer);
- request.reject(error);
- }
- pending = {};
- };
-
- const teardown = (error: Error) => {
- worker?.terminate();
- worker = null;
- ready = null;
- settleReady?.(error);
- settleReady = null;
- failAll(error);
- };
-
- const onMessage = (event: MessageEvent) => {
- const message = event.data ?? {};
- if (message.type === 'ready') {
- settleReady?.();
- settleReady = null;
- return;
- }
- if (message.type === 'error') {
- // The runtime failed to load (network, or the browser is unsupported).
- teardown(new Error(message.message));
- return;
- }
- const request = pending[message.id];
- if (!request) return;
- delete pending[message.id];
- clearTimeout(request.timer);
- if (message.error != null) {
- request.reject(new Error(message.error));
- } else {
- request.resolve(message.result);
- }
- };
-
- const spawn = () => {
- ready = new Promise((resolve, reject) => {
- settleReady = (error?: Error) => (error ? reject(error) : resolve());
- });
- worker = createWorkerFromContent(getWorkerSrc());
- worker.onmessage = onMessage;
- worker.onerror = (event) => teardown(new Error(`The Fortran worker crashed: ${event.message}`));
- };
-
- /** Spawn the worker if needed and resolve once the compiler has loaded. */
- const ensureReady = async () => {
- if (!ready) spawn();
- await ready;
- };
-
- const run = (code: string, input: string) =>
- ensureReady().then(
- () =>
- new Promise((resolve, reject) => {
- // `ensureReady` can resolve after the worker has been torn down (crash or timeout), so
- // the worker may be gone by the time the request is registered.
- if (!worker) {
- reject(new Error('The Fortran worker is not available'));
- return;
- }
- const id = nextId++;
- const timer = setTimeout(() => {
- // The worker hung — kill it and reject, so the next run respawns a fresh worker.
- teardown(new Error('The Fortran compiler timed out'));
- }, RUN_TIMEOUT_MS);
- pending[id] = { resolve, reject, timer };
- worker.postMessage({ id, code, input });
- }),
- );
-
- return { ensureReady, run };
-};
-
/**
* Reads the program out of the result page.
*
@@ -264,7 +162,9 @@ fortranWasm.run = async (input?: string) => {
return setResult(stdin, null, null, null);
}
- const runner = (fortranWasm.runner = fortranWasm.runner || createRunner());
+ const runner = (fortranWasm.runner =
+ fortranWasm.runner ||
+ createWorkerRunner({ getWorkerSrc, label: 'Fortran', timeoutMs: RUN_TIMEOUT_MS }));
try {
await ensureLoaded(runner);
diff --git a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
index 20e5072a87..3a4f0c4b16 100644
--- a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
+++ b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
@@ -1,6 +1,7 @@
/* eslint-disable no-console */
-import { createWorkerFromContent, getErrorMessage } from '../../utils/utils';
+import { getErrorMessage } from '../../utils/utils';
import { vWasmBaseUrl, vWasmClangBaseUrl } from '../../vendors';
+import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. The compiler is @live-codes/v-wasm, loaded from its CDN build: the
// IIFE defines `self.vWasm`, which the worker reaches with importScripts. It compiles V to C and
@@ -11,12 +12,6 @@ import { vWasmBaseUrl, vWasmClangBaseUrl } from '../../vendors';
// `importScripts`, so it has to run in a classic worker (the one createWorkerFromContent builds).
const SCRIPT_TYPE = 'text/v-wasm';
-interface WorkerRunResult {
- output: string;
- errors: string[];
- exitCode: number | null;
-}
-
interface RunResult {
input: string;
output: string | null;
@@ -24,11 +19,6 @@ interface RunResult {
exitCode: number | null;
}
-interface Runner {
- ensureReady: () => Promise;
- run: (code: string, input: string) => Promise;
-}
-
interface VWasmApi {
ready: boolean;
failed: boolean;
@@ -91,98 +81,6 @@ getCompiler().then(
// loop in the user program) by killing and respawning the worker.
const RUN_TIMEOUT_MS = 120000;
-interface Pending {
- resolve: (result: WorkerRunResult) => void;
- reject: (error: Error) => void;
- timer: ReturnType;
-}
-
-const createRunner = (): Runner => {
- let worker: Worker | null = null;
- let ready: Promise | null = null;
- let settleReady: ((error?: Error) => void) | null = null;
- let pending: Record = {};
- let nextId = 1;
-
- const failAll = (error: Error) => {
- for (const id of Object.keys(pending)) {
- const request = pending[Number(id)];
- clearTimeout(request.timer);
- request.reject(error);
- }
- pending = {};
- };
-
- const teardown = (error: Error) => {
- worker?.terminate();
- worker = null;
- ready = null;
- settleReady?.(error);
- settleReady = null;
- failAll(error);
- };
-
- const onMessage = (event: MessageEvent) => {
- const message = event.data ?? {};
- if (message.type === 'ready') {
- settleReady?.();
- settleReady = null;
- return;
- }
- if (message.type === 'error') {
- // The runtime failed to load (network, or the browser is unsupported).
- teardown(new Error(message.message));
- return;
- }
- const request = pending[message.id];
- if (!request) return;
- delete pending[message.id];
- clearTimeout(request.timer);
- if (message.error != null) {
- request.reject(new Error(message.error));
- } else {
- request.resolve(message.result);
- }
- };
-
- const spawn = () => {
- ready = new Promise((resolve, reject) => {
- settleReady = (error?: Error) => (error ? reject(error) : resolve());
- });
- worker = createWorkerFromContent(getWorkerSrc());
- worker.onmessage = onMessage;
- worker.onerror = (event) => teardown(new Error(`The V worker crashed: ${event.message}`));
- };
-
- /** Spawn the worker if needed and resolve once the compiler has loaded. */
- const ensureReady = async () => {
- if (!ready) spawn();
- await ready;
- };
-
- const run = (code: string, input: string) =>
- ensureReady().then(
- () =>
- new Promise((resolve, reject) => {
- // `ensureReady` can resolve after the worker has been torn down (crash or timeout), so
- // the worker may be gone by the time the request is registered.
- if (!worker) {
- reject(new Error('The V worker is not available'));
- return;
- }
- const id = nextId++;
- const timer = setTimeout(() => {
- // The worker hung — kill it and reject, so the next run respawns a fresh worker.
- teardown(new Error('The V compiler timed out'));
- }, RUN_TIMEOUT_MS);
- pending[id] = { resolve, reject, timer };
- worker.postMessage({ id, code, input });
- }),
- );
-
- return { ensureReady, run };
-};
-
/**
* Reads the program out of the result page.
*
@@ -297,7 +195,8 @@ vWasm.run = async (input?: string) => {
return setResult(stdin, null, null, null);
}
- const runner = (vWasm.runner = vWasm.runner || createRunner());
+ const runner = (vWasm.runner =
+ vWasm.runner || createWorkerRunner({ getWorkerSrc, label: 'V', timeoutMs: RUN_TIMEOUT_MS }));
try {
await ensureLoaded(runner);
diff --git a/src/livecodes/languages/worker-runner.ts b/src/livecodes/languages/worker-runner.ts
new file mode 100644
index 0000000000..bf77e7557f
--- /dev/null
+++ b/src/livecodes/languages/worker-runner.ts
@@ -0,0 +1,130 @@
+import { createWorkerFromContent } from '../utils/utils';
+
+/** The result a language worker reports for a single compile + run. */
+export interface WorkerRunResult {
+ output: string;
+ errors: string[];
+ exitCode: number | null;
+}
+
+export interface Runner {
+ ensureReady: () => Promise;
+ run: (code: string, input: string, options?: unknown) => Promise;
+ destroy: () => void;
+}
+
+export interface WorkerRunnerOptions {
+ /**
+ * The classic worker that loads the runtime. It must post `{ type: 'ready' | 'error' }` when the
+ * runtime has loaded (or failed to), and a run result keyed by the request id otherwise.
+ */
+ getWorkerSrc: () => string;
+ /** Shown in crash, timeout and unavailability messages, e.g. `Fortran`. */
+ label: string;
+ /** Kill the worker when a single run does not settle within this many milliseconds. */
+ timeoutMs?: number;
+}
+
+interface Pending {
+ resolve: (result: TResult) => void;
+ reject: (error: Error) => void;
+ timer?: ReturnType;
+}
+
+/**
+ * A serialized worker runner shared by the WASM languages. It keeps one worker for the compiler's
+ * lifetime, tears it down on crashes/timeouts and respawns it on the next run.
+ */
+export const createWorkerRunner = ({
+ getWorkerSrc,
+ label,
+ timeoutMs,
+}: WorkerRunnerOptions): Runner => {
+ let worker: Worker | null = null;
+ let ready: Promise | null = null;
+ let settleReady: ((error?: Error) => void) | null = null;
+ let pending: Record> = {};
+ let nextId = 1;
+
+ const failAll = (error: Error) => {
+ for (const id of Object.keys(pending)) {
+ const request = pending[Number(id)];
+ if (request.timer) clearTimeout(request.timer);
+ request.reject(error);
+ }
+ pending = {};
+ };
+
+ const teardown = (error?: Error) => {
+ worker?.terminate();
+ worker = null;
+ ready = null;
+ settleReady?.(error);
+ settleReady = null;
+ if (error) failAll(error);
+ };
+
+ const onMessage = (event: MessageEvent) => {
+ const message = event.data ?? {};
+ if (message.type === 'ready') {
+ settleReady?.();
+ settleReady = null;
+ return;
+ }
+ if (message.type === 'error') {
+ // The runtime failed to load (network, or the browser is unsupported).
+ teardown(new Error(message.message));
+ return;
+ }
+ const request = pending[message.id];
+ if (!request) return;
+ delete pending[message.id];
+ if (request.timer) clearTimeout(request.timer);
+ if (message.error != null) {
+ request.reject(new Error(message.error));
+ } else {
+ request.resolve(message.result);
+ }
+ };
+
+ const spawn = () => {
+ ready = new Promise((resolve, reject) => {
+ settleReady = (error?: Error) => (error ? reject(error) : resolve());
+ });
+ worker = createWorkerFromContent(getWorkerSrc());
+ worker.onmessage = onMessage;
+ worker.onerror = (event) =>
+ teardown(new Error(`The ${label} worker crashed: ${event.message}`));
+ };
+
+ /** Spawn the worker if needed and resolve once the runtime has loaded. */
+ const ensureReady = async () => {
+ if (!ready) spawn();
+ await ready;
+ };
+
+ const run = (code: string, input: string, options?: unknown) =>
+ ensureReady().then(
+ () =>
+ new Promise((resolve, reject) => {
+ // `ensureReady` can resolve after the worker has been torn down (crash or timeout), so
+ // the worker may be gone by the time the request is registered.
+ if (!worker) {
+ reject(new Error(`The ${label} worker is not available`));
+ return;
+ }
+ const id = nextId++;
+ const request: Pending = { resolve, reject };
+ if (timeoutMs != null) {
+ request.timer = setTimeout(() => {
+ // The worker hung, so kill it and reject; the next run respawns a fresh worker.
+ teardown(new Error(`The ${label} compiler timed out`));
+ }, timeoutMs);
+ }
+ pending[id] = request;
+ worker.postMessage({ id, code, input, options });
+ }),
+ );
+
+ return { ensureReady, run, destroy: () => teardown() };
+};
From 9d946106cfc3d3a042bf986008d12d72355058eb Mon Sep 17 00:00:00 2001
From: Hatem Hosny
Date: Tue, 6 Oct 2026 20:06:50 +0300
Subject: [PATCH 3/5] use same clang-wasm version across languages
---
.../languages/cobol-wasm/lang-cobol-wasm-script.ts | 4 ++--
src/livecodes/languages/v-wasm/lang-v-wasm-script.ts | 4 ++--
src/livecodes/vendors.ts | 10 +---------
3 files changed, 5 insertions(+), 13 deletions(-)
diff --git a/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts b/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts
index 720c6d53e7..4ad06e2c99 100644
--- a/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts
+++ b/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
import { getErrorMessage } from '../../utils/utils';
-import { cobolWasmBaseUrl, cobolWasmClangBaseUrl } from '../../vendors';
+import { clangWasmBaseUrl, cobolWasmBaseUrl } from '../../vendors';
import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. GnuCOBOL is the real compiler (@live-codes/cobol-wasm), loaded from
@@ -54,7 +54,7 @@ const getCompiler = () => {
self.cobolWasm.createCompiler({
baseUrl: ${JSON.stringify(cobolWasmBaseUrl + 'assets/')},
// The Clang half is pinned by the package, so it comes from that exact version's assets.
- clangBaseUrl: ${JSON.stringify(cobolWasmClangBaseUrl)},
+ clangBaseUrl: ${JSON.stringify(clangWasmBaseUrl + 'assets/')},
});
return compiler;
};
diff --git a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
index 3a4f0c4b16..7da023dedb 100644
--- a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
+++ b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
import { getErrorMessage } from '../../utils/utils';
-import { vWasmBaseUrl, vWasmClangBaseUrl } from '../../vendors';
+import { clangWasmBaseUrl, vWasmBaseUrl } from '../../vendors';
import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. The compiler is @live-codes/v-wasm, loaded from its CDN build: the
@@ -47,7 +47,7 @@ const getCompiler = () => {
self.vWasm.createCompiler({
baseUrl: ${JSON.stringify(vWasmBaseUrl + 'assets/v/')},
// The Clang half is pinned by the package, so it comes from that exact version's assets.
- clangBaseUrl: ${JSON.stringify(vWasmClangBaseUrl)},
+ clangBaseUrl: ${JSON.stringify(clangWasmBaseUrl + 'assets/')},
});
return compiler;
};
diff --git a/src/livecodes/vendors.ts b/src/livecodes/vendors.ts
index 7500a37d57..b6da5f97ce 100644
--- a/src/livecodes/vendors.ts
+++ b/src/livecodes/vendors.ts
@@ -62,7 +62,7 @@ export const cherryCljsBaseUrl = /* @__PURE__ */ getUrl('cherry-cljs@0.6.38/');
export const cjs2esUrl = /* @__PURE__ */ getUrl('cjs2es@1.1.1/dist/cjs2es.browser.js');
-export const clangWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/clang-wasm@0.2.0/');
+export const clangWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/clang-wasm@0.3.0/');
export const clioBaseUrl = /* @__PURE__ */ getUrl(
'@live-codes/clio-browser-compiler@0.0.3/public/build/',
@@ -104,10 +104,6 @@ export const cm6ThemeSolarizedDarkUrl = /* @__PURE__ */ getUrl(
export const cobolWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/cobol-wasm@0.1.0/');
-// `@live-codes/cobol-wasm` pins this exact `@live-codes/clang-wasm` version for the Clang half of its
-// toolchain, and the runtime re-derives those assets' digests, so the two must stay in step.
-export const cobolWasmClangBaseUrl = /* @__PURE__ */ getUrl('@live-codes/clang-wasm@0.3.0/assets/');
-
export const codeiumProviderUrl = /* @__PURE__ */ getUrl(
'@live-codes/monaco-codeium-provider@0.2.2/dist/index.js',
);
@@ -537,10 +533,6 @@ export const unicornJsX86Url = /* @__PURE__ */ getUrl(
export const uniterUrl = /* @__PURE__ */ getUrl('uniter@2.18.0/dist/uniter.js');
-// `@live-codes/v-wasm` bundles this exact `@live-codes/clang-wasm` version for the Clang half of its
-// pipeline, so its assets are fetched from that version rather than the one the C/C++ languages use.
-export const vWasmClangBaseUrl = /* @__PURE__ */ getUrl('@live-codes/clang-wasm@0.3.0/assets/');
-
export const vWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/v-wasm@0.1.0/');
export const vbWasmBaseUrl = /* @__PURE__ */ getUrl('@live-codes/vb-wasm@0.1.1/');
From d1dd76b6df877f85a3af9a7eff67073191f531f6 Mon Sep 17 00:00:00 2001
From: Hatem Hosny
Date: Tue, 6 Oct 2026 20:08:56 +0300
Subject: [PATCH 4/5] add monaco V language support
---
src/livecodes/languages/v-wasm/lang-v-wasm.ts | 6 ++----
src/livecodes/vendors.ts | 2 +-
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/livecodes/languages/v-wasm/lang-v-wasm.ts b/src/livecodes/languages/v-wasm/lang-v-wasm.ts
index bd4cf807a2..3622307e9a 100644
--- a/src/livecodes/languages/v-wasm/lang-v-wasm.ts
+++ b/src/livecodes/languages/v-wasm/lang-v-wasm.ts
@@ -14,11 +14,9 @@ export const vWasm: LanguageSpecs = {
extensions: ['v', 'vlang', 'v-wasm', 'wasm.v', 'vsh'],
editor: 'script',
editorSupport: {
- // There is no dedicated V mode in either editor, so the Go mode stands in: V's syntax is
- // deliberately close to Go's. Prism does ship a real V grammar, so CodeJar uses that.
monaco: {
- languageSupport: monacoLanguagesBaseUrl + 'go.js',
- language: 'go',
+ languageSupport: monacoLanguagesBaseUrl + 'v.js',
+ language: 'v',
},
codemirror: {
languageSupport: async () => (await import(codeMirrorBaseUrl + 'codemirror-lang-go.js')).go(),
diff --git a/src/livecodes/vendors.ts b/src/livecodes/vendors.ts
index b6da5f97ce..0d66238c54 100644
--- a/src/livecodes/vendors.ts
+++ b/src/livecodes/vendors.ts
@@ -360,7 +360,7 @@ export const monacoBaseUrl = /* @__PURE__ */ getUrl('@live-codes/monaco-editor@0
export const monacoEmacsUrl = /* @__PURE__ */ getUrl('monaco-emacs@0.3.0/dist/monaco-emacs.js');
export const monacoLanguagesBaseUrl = /* @__PURE__ */ getUrl(
- '@live-codes/monaco-languages@0.3.10/dist/',
+ '@live-codes/monaco-languages@0.3.11/dist/',
);
export const monacoThemesBaseUrl = /* @__PURE__ */ getUrl('monaco-themes@0.4.4/themes/');
From 565bb1fbebb6530aaad012b96217bc9382001868 Mon Sep 17 00:00:00 2001
From: "pullfrog[bot]" <226033991+pullfrog[bot]@users.noreply.github.com>
Date: Tue, 6 Oct 2026 17:23:19 +0000
Subject: [PATCH 5/5] fix: deduplicate WASM runtime boilerplate and correct V
Monaco spec
Extract getParentOrigin, createLoadingReporter and runCompiler into a shared wasm-runtime module and reuse them across the worker-runner WASM scripts (v-wasm, cobol, fortran, assembly, clang, fanak, haskell). This removes the duplicated blocks that pushed the SonarCloud new-code duplication above the 3% gate.
Also update the v-wasm spec to expect the Monaco 'v' language, which now ships in @live-codes/monaco-languages@0.3.11, replacing the stale 'go' expectation.
---
.../lang-assembly-wasm-script.ts | 21 +-----
.../clang-wasm/lang-clang-wasm-script.ts | 20 +-----
.../cobol-wasm/lang-cobol-wasm-script.ts | 41 ++----------
.../languages/fanak/lang-fanak-script.ts | 18 +----
.../fortran-wasm/lang-fortran-wasm-script.ts | 20 +-----
.../haskell-wasm/lang-haskell-wasm-script.ts | 15 +----
.../v-wasm/__tests__/lang-v-wasm.spec.ts | 4 +-
.../languages/v-wasm/lang-v-wasm-script.ts | 41 ++----------
src/livecodes/languages/wasm-runtime.ts | 67 +++++++++++++++++++
9 files changed, 88 insertions(+), 159 deletions(-)
create mode 100644 src/livecodes/languages/wasm-runtime.ts
diff --git a/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts b/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts
index b2b031da86..a01b634c8c 100644
--- a/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts
+++ b/src/livecodes/languages/assembly-wasm/lang-assembly-wasm-script.ts
@@ -1,11 +1,11 @@
/* eslint-disable no-console */
-import { getErrorMessage } from '../../utils/utils';
import {
assemblyWasmBaseUrl,
keystoneJsUrl,
keystoneJsWasmUrl,
unicornJsX86Url,
} from '../../vendors';
+import { runCompiler } from '../wasm-runtime';
import { createWorkerRunner, type Runner } from '../worker-runner';
// x86-64 assembly is assembled and executed by @live-codes/assembly-wasm, which loads
@@ -190,24 +190,7 @@ assemblyWasm.run = async (input?: string) => {
const runner = (assemblyWasm.runner =
assemblyWasm.runner || createWorkerRunner({ getWorkerSrc, label: 'Assembly' }));
- try {
- await ensureLoaded(runner);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
-
- try {
- const result = await runner.run(code, stdin);
- // `errors` holds the assembler's diagnostics and any runtime fault, and is empty
- // when the program assembled and exited cleanly.
- const errors = (result.errors || []).filter(Boolean);
- if (errors.length) {
- return setResult(stdin, null, errors.join('\n'), result.exitCode ?? 1);
- }
- return setResult(stdin, result.output ?? '', null, result.exitCode ?? 0);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
+ return runCompiler(runner, ensureLoaded, code, stdin, setResult);
};
// Alias, so both `livecodes.assemblyWasm` and `livecodes.asm` are available in markup.
diff --git a/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts b/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts
index 6d53ab349b..47177e7103 100644
--- a/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts
+++ b/src/livecodes/languages/clang-wasm/lang-clang-wasm-script.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
-import { getErrorMessage } from '../../utils/utils';
import { clangWasmBaseUrl } from '../../vendors';
+import { runCompiler } from '../wasm-runtime';
import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner serves all four Clang languages. The language is read from the script tags the
@@ -197,23 +197,7 @@ clangWasm.run = async (input?: string) => {
clangWasm.runners[language] ||
createWorkerRunner({ getWorkerSrc: () => getWorkerSrc(language), label: 'Clang' }));
- try {
- await ensureLoaded(runner);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
-
- try {
- const result = await runner.run(code, stdin, getSettings(language));
- // `errors` holds the compiler's diagnostics and is empty when the program compiled.
- const errors = (result.errors || []).filter(Boolean);
- if (errors.length) {
- return setResult(stdin, null, errors.join('\n'), result.exitCode ?? 1);
- }
- return setResult(stdin, result.output ?? '', null, result.exitCode ?? 0);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
+ return runCompiler(runner, ensureLoaded, code, stdin, setResult, getSettings(language));
};
// One alias per language, plus `cpp` for playgrounds written before the languages were split.
diff --git a/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts b/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts
index 4ad06e2c99..549284e28a 100644
--- a/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts
+++ b/src/livecodes/languages/cobol-wasm/lang-cobol-wasm-script.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
-import { getErrorMessage } from '../../utils/utils';
import { clangWasmBaseUrl, cobolWasmBaseUrl } from '../../vendors';
+import { createLoadingReporter, runCompiler } from '../wasm-runtime';
import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. GnuCOBOL is the real compiler (@live-codes/cobol-wasm), loaded from
@@ -133,25 +133,8 @@ cobolWasm.failed = false;
// ~25 MiB toolchain is not downloaded again, instead of spawning a new one.
cobolWasm.runner ??= null;
-// The result runs in an iframe; post status updates to the app origin. Mirrors
-// the other WASM language scripts (`fanak`, `haskell-wasm`).
-const parentOrigin =
- window.parent === window
- ? window.location.origin
- : window.location.ancestorOrigins?.[0] ||
- (() => {
- if (!document.referrer) return '*';
- try {
- return new URL(document.referrer).origin;
- } catch {
- // Ignore malformed referrers and use the wildcard fallback below.
- return '*';
- }
- })();
-
-const postLoading = (payload: boolean) => {
- parent.postMessage({ type: 'loading', payload }, parentOrigin); // NOSONAR - fallback is safe with source/origin checks in the parent.
-};
+// The result runs in an iframe; post status updates to the app origin.
+const postLoading = createLoadingReporter();
/** Start (once) downloading the compiler, showing the loading indicator while it happens. */
const ensureLoaded = (runner: Runner): Promise => {
@@ -206,23 +189,7 @@ cobolWasm.run = async (input?: string) => {
cobolWasm.runner ||
createWorkerRunner({ getWorkerSrc, label: 'COBOL', timeoutMs: RUN_TIMEOUT_MS }));
- try {
- await ensureLoaded(runner);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
-
- try {
- const result = await runner.run(code, stdin, cobolWasm.settings);
- // `errors` holds the compiler's diagnostics and is empty when the program ran.
- const errors = (result.errors || []).filter(Boolean);
- if (errors.length) {
- return setResult(stdin, null, errors.join('\n'), result.exitCode ?? 1);
- }
- return setResult(stdin, result.output ?? '', null, result.exitCode ?? 0);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
+ return runCompiler(runner, ensureLoaded, code, stdin, setResult, cobolWasm.settings);
};
// Aliases so both `livecodes.cobol` and `livecodes.cobolWasm` are available.
diff --git a/src/livecodes/languages/fanak/lang-fanak-script.ts b/src/livecodes/languages/fanak/lang-fanak-script.ts
index 72816c348d..b6635133e1 100644
--- a/src/livecodes/languages/fanak/lang-fanak-script.ts
+++ b/src/livecodes/languages/fanak/lang-fanak-script.ts
@@ -1,5 +1,6 @@
import { getErrorMessage } from '../../utils';
import { fanakBaseUrl } from '../../vendors';
+import { getParentOrigin } from '../wasm-runtime';
declare const livecodes: any;
@@ -24,21 +25,8 @@ const waitFor = async (condition: () => boolean | Promise, timeout = 60
return true;
};
-// The result runs in an iframe; post status updates to the app origin. Mirrors
-// the other WASM language scripts (`haskell-wasm`).
-const parentOrigin =
- window.parent === window
- ? window.location.origin
- : window.location.ancestorOrigins?.[0] ||
- (() => {
- if (!document.referrer) return '*';
- try {
- return new URL(document.referrer).origin;
- } catch {
- // Ignore malformed referrers and use the wildcard fallback below.
- return '*';
- }
- })();
+// The result runs in an iframe; post status updates to the app origin.
+const parentOrigin = getParentOrigin();
let activeRuns = 0;
const postLoading = (payload: boolean) => {
diff --git a/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts b/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts
index 5ba0b8e61c..2c237702aa 100644
--- a/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts
+++ b/src/livecodes/languages/fortran-wasm/lang-fortran-wasm-script.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
-import { getErrorMessage } from '../../utils/utils';
import { fortranWasmBaseUrl } from '../../vendors';
+import { runCompiler } from '../wasm-runtime';
import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. The compiler itself is @live-codes/lfortran-wasm, loaded from its
@@ -166,23 +166,7 @@ fortranWasm.run = async (input?: string) => {
fortranWasm.runner ||
createWorkerRunner({ getWorkerSrc, label: 'Fortran', timeoutMs: RUN_TIMEOUT_MS }));
- try {
- await ensureLoaded(runner);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
-
- try {
- const result = await runner.run(code, stdin);
- // `errors` holds the compiler's diagnostics and is empty when the program ran.
- const errors = (result.errors || []).filter(Boolean);
- if (errors.length) {
- return setResult(stdin, null, errors.join('\n'), result.exitCode ?? 1);
- }
- return setResult(stdin, result.output ?? '', null, result.exitCode ?? 0);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
+ return runCompiler(runner, ensureLoaded, code, stdin, setResult);
};
window.livecodes.fortran = fortranWasm;
diff --git a/src/livecodes/languages/haskell-wasm/lang-haskell-wasm-script.ts b/src/livecodes/languages/haskell-wasm/lang-haskell-wasm-script.ts
index 4bf5f38bab..e4a6f10ee3 100644
--- a/src/livecodes/languages/haskell-wasm/lang-haskell-wasm-script.ts
+++ b/src/livecodes/languages/haskell-wasm/lang-haskell-wasm-script.ts
@@ -1,5 +1,6 @@
import { getErrorMessage } from '../../utils/utils';
import { ghcBrowserBaseUrl, haskellWasiShimUrl } from '../../vendors';
+import { getParentOrigin } from '../wasm-runtime';
// @ts-ignore
// eslint-disable-next-line import/no-unresolved
import workerContent from './lang-haskell-wasm-worker.raw.js?raw';
@@ -135,19 +136,7 @@ const createRunner = (createWorker: () => Worker) => {
return { init, run };
};
-const parentOrigin =
- window.parent === window
- ? window.location.origin
- : window.location.ancestorOrigins?.[0] ||
- (() => {
- if (!document.referrer) return '*';
- try {
- return new URL(document.referrer).origin;
- } catch {
- // Ignore malformed referrers and use the wildcard fallback below.
- return '*';
- }
- })();
+const parentOrigin = getParentOrigin();
window.livecodes.haskellWasm ??= {};
const haskellWasm = window.livecodes.haskellWasm;
diff --git a/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts b/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts
index c89cfb163b..ec6348854e 100644
--- a/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts
+++ b/src/livecodes/languages/v-wasm/__tests__/lang-v-wasm.spec.ts
@@ -24,8 +24,8 @@ describe('V (Wasm) language spec', () => {
test('uses a code editor with V support', () => {
expect(vWasm.editor).toBe('script');
- // There is no dedicated V mode, so the Go mode stands in for the editors that lack one.
- expect(vWasm.editorSupport?.monaco?.language).toBe('go');
+ // Monaco ships a V mode; CodeMirror has none, so the Go mode stands in there.
+ expect(vWasm.editorSupport?.monaco?.language).toBe('v');
// Prism does ship a V grammar, so CodeJar uses the real one.
expect(vWasm.editorSupport?.codejar?.language).toBe('v');
});
diff --git a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
index 7da023dedb..26dc50fec7 100644
--- a/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
+++ b/src/livecodes/languages/v-wasm/lang-v-wasm-script.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
-import { getErrorMessage } from '../../utils/utils';
import { clangWasmBaseUrl, vWasmBaseUrl } from '../../vendors';
+import { createLoadingReporter, runCompiler } from '../wasm-runtime';
import { createWorkerRunner, type Runner } from '../worker-runner';
// One runner for one language. The compiler is @live-codes/v-wasm, loaded from its CDN build: the
@@ -127,25 +127,8 @@ vWasm.failed = false;
// V compiler plus Clang toolchain are not downloaded again, instead of spawning a new one.
vWasm.runner ??= null;
-// The result runs in an iframe; post status updates to the app origin. Mirrors
-// the other WASM language scripts (`fanak`, `haskell-wasm`).
-const parentOrigin =
- window.parent === window
- ? window.location.origin
- : window.location.ancestorOrigins?.[0] ||
- (() => {
- if (!document.referrer) return '*';
- try {
- return new URL(document.referrer).origin;
- } catch {
- // Ignore malformed referrers and use the wildcard fallback below.
- return '*';
- }
- })();
-
-const postLoading = (payload: boolean) => {
- parent.postMessage({ type: 'loading', payload }, parentOrigin); // NOSONAR - fallback is safe with source/origin checks in the parent.
-};
+// The result runs in an iframe; post status updates to the app origin.
+const postLoading = createLoadingReporter();
/** Start (once) downloading the compiler, showing the loading indicator while it happens. */
const ensureLoaded = (runner: Runner): Promise => {
@@ -198,23 +181,7 @@ vWasm.run = async (input?: string) => {
const runner = (vWasm.runner =
vWasm.runner || createWorkerRunner({ getWorkerSrc, label: 'V', timeoutMs: RUN_TIMEOUT_MS }));
- try {
- await ensureLoaded(runner);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
-
- try {
- const result = await runner.run(code, stdin);
- // `errors` holds the compiler's diagnostics and is empty when the program ran.
- const errors = (result.errors || []).filter(Boolean);
- if (errors.length) {
- return setResult(stdin, null, errors.join('\n'), result.exitCode ?? 1);
- }
- return setResult(stdin, result.output ?? '', null, result.exitCode ?? 0);
- } catch (error) {
- return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
- }
+ return runCompiler(runner, ensureLoaded, code, stdin, setResult);
};
window.livecodes.v = vWasm;
diff --git a/src/livecodes/languages/wasm-runtime.ts b/src/livecodes/languages/wasm-runtime.ts
new file mode 100644
index 0000000000..253e932aa1
--- /dev/null
+++ b/src/livecodes/languages/wasm-runtime.ts
@@ -0,0 +1,67 @@
+import { getErrorMessage } from '../utils/utils';
+import type { Runner } from './worker-runner';
+
+/**
+ * The origin the result page posts status messages to. The result runs in an iframe, so the app
+ * origin is the parent's origin, the first ancestor's, or the referrer's; the wildcard is a last
+ * resort and is only safe because the parent checks the source/origin of incoming messages.
+ */
+export const getParentOrigin = () =>
+ window.parent === window
+ ? window.location.origin
+ : window.location.ancestorOrigins?.[0] ||
+ (() => {
+ if (!document.referrer) return '*';
+ try {
+ return new URL(document.referrer).origin;
+ } catch {
+ // Ignore malformed referrers and use the wildcard fallback below.
+ return '*';
+ }
+ })();
+
+/** Reports loading-state changes to the app origin. */
+export const createLoadingReporter = () => {
+ const parentOrigin = getParentOrigin();
+ return (payload: boolean) => {
+ parent.postMessage({ type: 'loading', payload }, parentOrigin); // NOSONAR - fallback is safe with source/origin checks in the parent.
+ };
+};
+
+export type SetResult = (
+ input: string,
+ output: string | null,
+ error: string | null,
+ exitCode: number | null,
+) => TResult;
+
+/**
+ * Runs `code` on a ready runner and maps the outcome through `setResult`, reporting both the
+ * compiler's diagnostics and any failure to load the runtime as the result's error.
+ */
+export const runCompiler = async (
+ runner: Runner,
+ ensureLoaded: (runner: Runner) => Promise,
+ code: string,
+ stdin: string,
+ setResult: SetResult,
+ options?: unknown,
+): Promise => {
+ try {
+ await ensureLoaded(runner);
+ } catch (error) {
+ return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
+ }
+
+ try {
+ const result = await runner.run(code, stdin, options);
+ // `errors` holds the compiler's diagnostics and is empty when the program ran.
+ const errors = (result.errors || []).filter(Boolean);
+ if (errors.length) {
+ return setResult(stdin, null, errors.join('\n'), result.exitCode ?? 1);
+ }
+ return setResult(stdin, result.output ?? '', null, result.exitCode ?? 0);
+ } catch (error) {
+ return setResult(stdin, null, `Error: ${getErrorMessage(error)}`, 1);
+ }
+};