diff --git a/package-lock.json b/package-lock.json
index 893657f..4b03476 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,12 +13,14 @@
"@codemirror/autocomplete": "^6.19.1",
"@codemirror/lang-rust": "^6.0.2",
"@codemirror/language": "^6.11.3",
+ "@codemirror/lint": "^6.9.5",
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.6",
"@fontsource/poppins": "^5.2.7",
"@lezer/highlight": "^1.2.2",
"@lezer/rust": "^1.0.2",
"@microsoft/clarity": "^1.0.2",
+ "@replit/codemirror-vim": "^6.3.0",
"@tailwindcss/vite": "^4.1.16",
"astro": "^5.15.0",
"codemirror": "^6.0.2",
@@ -563,9 +565,9 @@
}
},
"node_modules/@codemirror/lint": {
- "version": "6.9.2",
- "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz",
- "integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==",
+ "version": "6.9.5",
+ "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.5.tgz",
+ "integrity": "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
@@ -1686,6 +1688,19 @@
"object-assign": "^4.1.1"
}
},
+ "node_modules/@replit/codemirror-vim": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.3.0.tgz",
+ "integrity": "sha512-aTx931ULAMuJx6xLf7KQDOL7CxD+Sa05FktTDrtLaSy53uj01ll3Zf17JdKsriER248oS55GBzg0CfCTjEneAQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@codemirror/commands": "6.x.x",
+ "@codemirror/language": "6.x.x",
+ "@codemirror/search": "6.x.x",
+ "@codemirror/state": "6.x.x",
+ "@codemirror/view": "6.x.x"
+ }
+ },
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
"license": "MIT",
diff --git a/package.json b/package.json
index 027904e..957d71a 100644
--- a/package.json
+++ b/package.json
@@ -18,12 +18,14 @@
"@codemirror/autocomplete": "^6.19.1",
"@codemirror/lang-rust": "^6.0.2",
"@codemirror/language": "^6.11.3",
+ "@codemirror/lint": "^6.9.5",
"@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.6",
"@fontsource/poppins": "^5.2.7",
"@lezer/highlight": "^1.2.2",
"@lezer/rust": "^1.0.2",
"@microsoft/clarity": "^1.0.2",
+ "@replit/codemirror-vim": "^6.3.0",
"@tailwindcss/vite": "^4.1.16",
"astro": "^5.15.0",
"codemirror": "^6.0.2",
diff --git a/src/components/editor/CodeEditor.astro b/src/components/editor/CodeEditor.astro
index 9dd1f99..7b43778 100644
--- a/src/components/editor/CodeEditor.astro
+++ b/src/components/editor/CodeEditor.astro
@@ -7,94 +7,320 @@ interface Props {
const { slug, initialCode = 'fn main() {\n println!("Hola, mundo!");\n}\n' } = Astro.props
---
-
+
-
diff --git a/src/components/editor/output/Terminal.astro b/src/components/editor/output/Terminal.astro
index 00cb371..28d40e1 100644
--- a/src/components/editor/output/Terminal.astro
+++ b/src/components/editor/output/Terminal.astro
@@ -2,6 +2,7 @@
import IconLoader2 from "~icons/tabler/loader-2"
import IconPlayerPlay from "~icons/tabler/player-play"
import IconRestore from "~icons/tabler/restore"
+import IconSparkles from "~icons/tabler/sparkles"
import IconTerminal from "~icons/tabler/terminal"
interface Props {
@@ -47,6 +48,20 @@ const isTestMode = !!testCode
Reiniciar
+
+
Reiniciar
+
+
+
+
{t("profile.editor_section")}
+
+
+
+ {t("profile.vim_mode_label")}
+ {t("profile.vim_mode_desc")}
+
+
+
+
@@ -330,6 +353,24 @@ const trackMap = Object.fromEntries(tracks.map((t) => [t.id, t]))
}
})
+ // Vim mode toggle
+ const vimToggle = document.getElementById("vim-toggle") as HTMLButtonElement
+ const getPrefs = () => JSON.parse(localStorage.getItem("preferences") ?? "{}")
+ const savePrefs = (prefs: Record) => localStorage.setItem("preferences", JSON.stringify(prefs))
+
+ const vimEnabled = !!getPrefs().vimEditor
+ vimToggle.setAttribute("aria-checked", String(vimEnabled))
+ vimToggle.querySelector("span")!.dataset.ariaChecked = String(vimEnabled)
+
+ vimToggle.addEventListener("click", () => {
+ const prefs = getPrefs()
+ const next = !prefs.vimEditor
+ prefs.vimEditor = next
+ savePrefs(prefs)
+ vimToggle.setAttribute("aria-checked", String(next))
+ window.dispatchEvent(new CustomEvent("preferences:changed", { detail: { vimEditor: next } }))
+ })
+
document.querySelectorAll("[data-copy-link]").forEach((btn) => {
btn.addEventListener("click", async () => {
const path = btn.dataset.copyLink!