Created
August 27, 2026 23:41
-
-
Save eduardoarandah/4dbf33da3fe60042ee0e21695ef64b10 to your computer and use it in GitHub Desktop.
luasnip snippets to create forms in markdown, useful for agents
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- https://github.com/L3MON4D3/LuaSnip/blob/master/Examples/snippets.lua | |
| -- extract functions from luasnips | |
| local ls = require("luasnip") | |
| local snippet = ls.snippet | |
| local text_node = ls.text_node | |
| local insert_node = ls.insert_node | |
| local function_node = ls.function_node | |
| local fmt = require("luasnip.extras.fmt").fmt | |
| -------------------------------------------------------------------------------- | |
| -- helpers | |
| -------------------------------------------------------------------------------- | |
| local PANEL_WIDTH = 72 | |
| local function dw(s) | |
| return vim.fn.strdisplaywidth(s or "") | |
| end | |
| local function pad_right(s, width) | |
| return s .. string.rep(" ", math.max(0, width - dw(s))) | |
| end | |
| --- Cola del borde superior: rellena con ─ hasta PANEL_WIDTH según el título. | |
| local function top_border_tail(args) | |
| local title_width = dw(args[1][1]) | |
| return " " .. string.rep("─", math.max(1, PANEL_WIDTH - 5 - title_width)) .. "┐" | |
| end | |
| return { | |
| -------------------------------------------------------------------------------- | |
| --- Forms | |
| -------------------------------------------------------------------------------- | |
| -- Panel de una sola línea, editable en vivo (el borde se reajusta al escribir). | |
| snippet("panel_line", { | |
| text_node("┌─ "), | |
| insert_node(1, "Título"), | |
| function_node(top_border_tail, { 1 }), | |
| text_node({ "", "│ " }), | |
| insert_node(2, "contenido"), | |
| function_node(function(args) | |
| return pad_right("", PANEL_WIDTH - 4 - dw(args[1][1])) .. " │" | |
| end, { 2 }), | |
| text_node({ "", "└" .. string.rep("─", PANEL_WIDTH - 2) .. "┘" }), | |
| }, { | |
| desc = "Panel con título de una sola línea de contenido", | |
| }), | |
| -- Panel vacío de N líneas para rellenar a mano. | |
| snippet("panel_blank", { | |
| text_node("┌─ "), | |
| insert_node(1, "Título"), | |
| function_node(top_border_tail, { 1 }), | |
| text_node({ | |
| "", | |
| "│ " .. string.rep(" ", PANEL_WIDTH - 4) .. " │", | |
| "│ " .. string.rep(" ", PANEL_WIDTH - 4) .. " │", | |
| "│ " .. string.rep(" ", PANEL_WIDTH - 4) .. " │", | |
| "└" .. string.rep("─", PANEL_WIDTH - 2) .. "┘", | |
| }), | |
| }, { | |
| desc = "Panel vacío con título", | |
| }), | |
| -- Leyenda de notación: ponla una vez arriba del wireframe. | |
| snippet("form_legend", { | |
| text_node({ | |
| "Notación:", | |
| " [ ] / [x] checkbox ( ) / (•) radio (●—) toggle activo", | |
| " [__________] texto [ 0 ] número (rango) [ 2026-01-29 📅 ] fecha", | |
| " [ Opción ▾ ] select ┌─┐ textarea [ 📎 Adjuntar… ] archivo", | |
| " 【 Primario 】 ( Secundario ) « solo lectura / calculado » 🔒 deshabilitado", | |
| " * requerido ↳ ayuda o validación ⟳ fila repetible", | |
| "", | |
| }), | |
| }, { | |
| desc = "Leyenda de la notación de controles", | |
| }), | |
| -- Andamio completo de pantalla. | |
| snippet( | |
| "form_screen", | |
| fmt( | |
| [================================================[ | |
| ## <1> | |
| <2>* [__________] | |
| <3> [ <4> ▾ ] | |
| ↳ opciones: <5> | |
| 【 Guardar 】 ( Cancelar ) | |
| ]================================================], | |
| { | |
| insert_node(1, "TÍTULO DE LA PANTALLA"), | |
| insert_node(2, "Campo"), | |
| insert_node(3, "Otro campo"), | |
| insert_node(4, "Opción por defecto"), | |
| insert_node(5, "A / B / C"), | |
| }, | |
| { delimiters = "<>" } | |
| ), | |
| { desc = "Andamio de pantalla de formulario" } | |
| ), | |
| -- Controles sueltos | |
| snippet("form_text", fmt("<1>* [__________]", { insert_node(1, "Etiqueta") }, { delimiters = "<>" }), { desc = "Input de texto" }), | |
| snippet("form_number", fmt("<1>* [ <2> ] (<3>)", { insert_node(1, "Etiqueta"), insert_node(2, "0"), insert_node(3, "0–100, entero") }, { delimiters = "<>" }), { desc = "Input numérico con rango" }), | |
| snippet( | |
| "form_checkbox", | |
| fmt( | |
| [================================================[ | |
| <1> | |
| [x] <2> | |
| [ ] <3> | |
| [ ] <4> | |
| ↳ <5> | |
| ]================================================], | |
| { | |
| insert_node(1, "Etiqueta del grupo"), | |
| insert_node(2, "Opción A"), | |
| insert_node(3, "Opción B"), | |
| insert_node(4, "Opción C"), | |
| insert_node(5, "al menos una"), | |
| }, | |
| { delimiters = "<>" } | |
| ), | |
| { desc = "Grupo de checkboxes" } | |
| ), | |
| snippet( | |
| "form_radio", | |
| fmt("<1>* (•) <2> ( ) <3> ( ) <4>", { | |
| insert_node(1, "Etiqueta"), | |
| insert_node(2, "Opción A"), | |
| insert_node(3, "Opción B"), | |
| insert_node(4, "Opción C"), | |
| }, { delimiters = "<>" }), | |
| { desc = "Grupo de radios en línea" } | |
| ), | |
| snippet( | |
| "form_select", | |
| fmt( | |
| [================================================[ | |
| <1>* [ <2> ▾ ] | |
| ↳ opciones: <3> | |
| ]================================================], | |
| { | |
| insert_node(1, "Etiqueta"), | |
| insert_node(2, "Valor por defecto"), | |
| insert_node(3, "A / B / C"), | |
| }, | |
| { delimiters = "<>" } | |
| ), | |
| { desc = "Select con lista de opciones" } | |
| ), | |
| snippet( | |
| "form_textarea", | |
| fmt( | |
| [================================================[ | |
| <1> | |
| ┌──────────────────────────────────────────────────┐ | |
| │ │ | |
| │ │ | |
| └──────────────────────────────────────────────────┘ | |
| ↳ <2> | |
| ]================================================], | |
| { insert_node(1, "Etiqueta"), insert_node(2, "máx. 500 caracteres") }, | |
| { delimiters = "<>" } | |
| ), | |
| { desc = "Textarea" } | |
| ), | |
| snippet( | |
| "form_toggle", | |
| fmt("<1> (●—) <2> / (—●) <3>", { | |
| insert_node(1, "Etiqueta"), | |
| insert_node(2, "activo"), | |
| insert_node(3, "inactivo"), | |
| }, { delimiters = "<>" }), | |
| { desc = "Toggle" } | |
| ), | |
| snippet( | |
| "form_date", | |
| fmt("<1>* [ <2> 📅 ]", { | |
| insert_node(1, "Fecha"), | |
| function_node(function() | |
| return os.date("%Y-%m-%d") | |
| end), | |
| }, { delimiters = "<>" }), | |
| { desc = "Input de fecha" } | |
| ), | |
| snippet( | |
| "form_file", | |
| fmt("<1> [ 📎 <2> ] ↳ <3>", { | |
| insert_node(1, "Adjuntos"), | |
| insert_node(2, "Seleccionar archivo…"), | |
| insert_node(3, "pdf, jpg — máx. 5 MB"), | |
| }, { delimiters = "<>" }), | |
| { desc = "Input de archivo" } | |
| ), | |
| snippet("form_readonly", fmt("<1> « <2> »", { insert_node(1, "Etiqueta"), insert_node(2, "valor calculado") }, { delimiters = "<>" }), { desc = "Campo de solo lectura / calculado" }), | |
| snippet("form_actions", fmt("【 <1> 】 ( <2> )", { insert_node(1, "Guardar"), insert_node(2, "Cancelar") }, { delimiters = "<>" }), { desc = "Botones primario y secundario" }), | |
| snippet("form_rule", fmt("↳ <1>", { insert_node(1, "regla de validación") }, { delimiters = "<>" }), { desc = "Línea de ayuda / validación" }), | |
| -- Tabla de filas repetibles. | |
| snippet( | |
| "form_table", | |
| fmt( | |
| [================================================[ | |
| ⟳ una fila por <1> | |
| | <2> | <3> | <4> | | |
| | ----- | -------- | ----- | | |
| | valor | [ 0 ] | [ ▾ ] | | |
| | valor | [ 0 ] | [ ▾ ] | | |
| ]================================================], | |
| { | |
| insert_node(1, "partida"), | |
| insert_node(2, "Columna A"), | |
| insert_node(3, "Columna B"), | |
| insert_node(4, "Columna C"), | |
| }, | |
| { delimiters = "<>" } | |
| ), | |
| { desc = "Tabla markdown de filas repetibles" } | |
| ), | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment