diff --git a/packages/preview/tsiib/0.1.0/.gitkeep b/packages/preview/tsiib/0.1.0/.gitkeep new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/.gitkeep @@ -0,0 +1 @@ + diff --git a/packages/preview/tsiib/0.1.0/LICENSE b/packages/preview/tsiib/0.1.0/LICENSE new file mode 100644 index 0000000000..bbb7563941 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 M.H. Alberto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/preview/tsiib/0.1.0/README.md b/packages/preview/tsiib/0.1.0/README.md new file mode 100644 index 0000000000..20d0449a1c --- /dev/null +++ b/packages/preview/tsiib/0.1.0/README.md @@ -0,0 +1,248 @@ +# tsiib + +Plantilla tipográfica para artículos académicos en español con estilo LaTeX, +para [Typst](https://typst.app) v0.15+. + +## Instalación + +### Desde Typst Universe + +Una vez publicado, se importa con: + +```typst +#import "@preview/tsiib:0.1.0": * +``` + +### Instalación local (para desarrollo o antes de la publicación) + +Copia la carpeta del paquete a la ruta local de Typst: + +| Sistema | Ruta | +|---------|------| +| Windows | `%APPDATA%\typst\packages\local\tsiib\0.1.0\` | +| macOS | `~/Library/Application Support/typst/packages/local/tsiib/0.1.0/` | +| Linux | `~/.local/share/typst/packages/local/tsiib/0.1.0/` | + +Luego importa con `@local`: + +```typst +#import "@local/tsiib:0.1.0": * +``` + +### Uso directo (sin instalar) + +Descarga el repositorio de [GitHub](https://github.com/MHAlberto/tsiib) e +importa `lib.typ` con ruta relativa: + +```typst +#import "ruta/a/tsiib/lib.typ": * +``` + +## Uso rápido + +```typst +#import "@local/tsiib:0.1.0": * + +#show: tsiib.with( + title: "Título del Artículo", + subtitle: "Subtítulo opcional", + author: "Tu Nombre", + affiliation: "Universidad o Instituto", + email: "correo@ejemplo.com", + date: "1 de enero de 2026", + abstract: [Resumen del artículo...], + keywords: "palabras, clave", // solo metadato PDF + bib: bibliography("refs.bib"), +) + += Introducción + +#definicion[ + Una *función continua* es aquella donde límite y evaluación conmutan. +] + +En la Definición @def-continua se establece la propiedad fundamental... +``` + +## Parámetros de la plantilla `tsiib()` + +| Parámetro | Tipo | Requerido | Descripción | +|--------------|----------|-----------|----------------------------------------------------------------| +| `title` | string | Sí | Título del documento | +| `subtitle` | string | No | Subtítulo del documento | +| `author` | string | No | Nombre del autor | +| `affiliation`| string | No | Afiliación del autor (universidad, instituto) | +| `email` | string | No | Correo del autor (se renderiza como `mailto:`) | +| `date` | string | No | Fecha (por defecto: fecha actual en español) | +| `abstract` | content | No | Resumen del artículo | +| `keywords` | string | No | Palabras clave (solo metadato PDF, no se muestra) | +| `bib` | content | No | Contenido de bibliografía: `bibliography("refs.bib")` | +| `body` | content | Sí | Contenido del documento | + +## Macros disponibles + +### Entornos matemáticos + +| Macro | Numeración | Descripción | +|-------|-----------|-------------| +| `teorema(titulo: none, cuerpo)` | Dependiente de sección | Teorema. Comparte contador con proposición, corolario y axioma | +| `proposicion(titulo: none, cuerpo)` | Dependiente de sección | Proposición | +| `corolario(titulo: none, cuerpo)` | Dependiente de sección | Corolario | +| `axioma(titulo: none, cuerpo)` | Dependiente de sección | Axioma | +| `lema(titulo: none, cuerpo)` | Dependiente de sección | Lema con contador propio | +| `definicion(titulo: none, cuerpo)` | Dependiente de sección | Definición con contador propio | +| `ejemplo(titulo: none, cuerpo)` | Dependiente de sección | Ejemplo con contador propio | +| `problema(titulo: none, cuerpo)` | Dependiente de sección | Problema con contador propio | +| `demostracion(cuerpo)` | No numerado | Demostración con QED `$square$` alineado a la derecha | +| `solucion(cuerpo)` | No numerado | Solución en texto normal | + +Cada entorno numerado acepta un título opcional: + +```typst +#teorema(titulo: "del Valor Medio")[ + Sea $f$ continua en $[a,b]$... +] +// Renderiza: Teorema 1.1 (del Valor Medio). Sea $f$... +``` + +### Figuras y tablas + +| Macro | Descripción | +|-------|------------| +| `figura(body, caption: none)` | Figura auto-numerada con caption abajo | +| `tabla(caption: none, columns: auto, ..body)` | Tabla estilo booktabs (LaTeX profesional) | +| `imagen(path, width: 100%, caption: none)` | Atajo para imágenes desde archivo | +| `figura-izq-texto(ruta, caption, img-width, gutter, texto)` | Imagen a la izquierda, texto a la derecha | +| `figura-der-texto(ruta, caption, img-width, gutter, texto)` | Imagen a la derecha, texto a la izquierda | + +### Ecuaciones numeradas + +Las ecuaciones display (`$ ... $`) por defecto **no** llevan numeración. Para +obtener una ecuación numerada y referenciable se usa la macro `ecuacion()`: + +```typst +#ecuacion[$ E = m c^2 $] + +Como se observa en la ecuación @eq-einstein... +``` + +### Bloques de código + +| Macro | Descripción | +|-------|------------| +| `codigo-formal(codigo, lang: "python", titulo: none)` | Código con números de línea, fondo gris, y título opcional como «Listado X.Y» | + +```typst +#codigo-formal(``` +import pandas as pd +df = pd.read_csv("datos.csv") +```) + +#codigo-formal(titulo: "Función de activación", ``` +def relu(x): + return max(0, x) +```) +``` + +### Referencias cruzadas + +Todas las referencias (`@label`), citas bibliográficas (`@bibkey`), y +entradas del índice se renderizan en color azul (`#2563eb`). + +Cada entorno numerado acepta un label después de la macro: + +```typst +#teorema[ + Sea $f$ continua en $[a,b]$... +] + +Como se demostró en el Teorema @teo-importante... +``` + +La referencia `@teo-importante` se renderiza como *Teorema 1.1*, incluyendo +el número de sección principal en formato homogéneo "Nombre X.Y". + +### Bibliografía + +Se pasa el resultado de `bibliography()` al parámetro `bib`. La ruta al +archivo `.bib` se resuelve desde tu documento: + +```typst +#show: tsiib.with( + title: "Mi Artículo", + author: "Autor", + bib: bibliography("refs.bib"), +) +``` + +## Estructura del proyecto + +``` +tsiib/ +├── typst.toml Metadatos del paquete +├── lib.typ Punto de entrada +├── main.typ Artículo de ejemplo +├── refs.bib Bibliografía de ejemplo +├── cuadrado.png Imagen de ejemplo +├── LICENSE Licencia MIT +├── README.md +├── .gitignore +└── src/ + ├── template.typ Plantilla principal tsiib() + ├── counters.typ Contadores y numbering + ├── macros.typ Entornos matemáticos + ├── figures.typ Figuras, tablas e imágenes + ├── codigo.typ Bloques de código + └── links.typ Hipervínculos +``` + +## Detalles técnicos + +### Contadores + +Los entornos numerados usan `figure()` con distintos valores de `kind`: + +| Entorno | Figure kind | Comparte con | Formato de @ref | +|-------------|-------------|-------------|-----------------| +| Teorema | `teo` | Prop, Cor, Ax | Teorema 1.1 | +| Proposición | `teo` | Teo, Cor, Ax | Proposición 1.2 | +| Corolario | `teo` | Teo, Prop, Ax | Corolario 1.3 | +| Axioma | `teo` | Teo, Prop, Cor| Axioma 1.4 | +| Definición | `def` | — | Definición 1.1 | +| Ejemplo | `ej` | — | Ejemplo 1.1 | +| Problema | `prob` | — | Problema 1.1 | +| Lema | `lema` | — | Lema 1.1 | +| Figura | `image` | — | Figura 1.1 | +| Tabla | `table` | — | Tabla 1.1 | +| Listado | `code` | — | Listado 1.1 | + +Los contadores se reinician en cada sección principal (`=`). +El formato es siempre "Nombre X.Y": X = número de sección, Y = secuencial. + +### Tipografía y diseño + +- **Fuente:** New Computer Modern (texto) + New Computer Modern Math (ecuaciones) +- **Tamaño:** 11pt, justificado, sangría de 1.5em +- **Papel:** US Letter, márgenes de 3cm +- **Encabezado:** Título del artículo en cursiva (excepto página 1) +- **Color de enlaces:** Azul `#2563eb` + +## Compilar el ejemplo + +```bash +git clone https://github.com/MHAlberto/tsiib.git +cd tsiib +typst compile main.typ +``` + +## Publicar en Typst Universe + +1. Haz un fork de [`typst/packages`](https://github.com/typst/packages) +2. Crea la carpeta `packages/preview/tsiib/0.1.0/` +3. Copia solo los archivos del paquete: `typst.toml`, `lib.typ`, `src/` + (no incluyas `main.typ`, `refs.bib`, `cuadrado.png`, `README.md`, `LICENSE`) +4. Abre un Pull Request + +## Licencia + +MIT © M.H. Alberto diff --git a/packages/preview/tsiib/0.1.0/cuadrado.png b/packages/preview/tsiib/0.1.0/cuadrado.png new file mode 100644 index 0000000000..3f70bd1c73 Binary files /dev/null and b/packages/preview/tsiib/0.1.0/cuadrado.png differ diff --git a/packages/preview/tsiib/0.1.0/lib.typ b/packages/preview/tsiib/0.1.0/lib.typ new file mode 100644 index 0000000000..6c6e5b6611 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/lib.typ @@ -0,0 +1,12 @@ +// ========================================== +// tsiib — Paquete tipográfico para artículos en español +// Punto de entrada principal +// ========================================== + +#import "src/template.typ": tsiib + +#import "src/macros.typ": teorema, proposicion, corolario, lema, problema, ejemplo, definicion, demostracion, solucion, axioma, ecuacion + +#import "src/figures.typ": figura, tabla, imagen, figura-izq-texto, figura-der-texto + +#import "src/codigo.typ": codigo-formal diff --git a/packages/preview/tsiib/0.1.0/main.typ b/packages/preview/tsiib/0.1.0/main.typ new file mode 100644 index 0000000000..582dc6e10a --- /dev/null +++ b/packages/preview/tsiib/0.1.0/main.typ @@ -0,0 +1,270 @@ +// ========================================== +// main.typ — Artículo de ejemplo usando tsiib +// Tema: Regresión Lineal +// ========================================== + +#import "@preview/tsiib:0.1.0": tsiib, teorema, proposicion, corolario, lema, problema, ejemplo, definicion, demostracion, solucion, axioma, ecuacion, figura, tabla, imagen, codigo-formal + +#show: tsiib.with( + title: "Fundamentos Matemáticos de la Regresión Lineal", + subtitle: "Una Perspectiva Algebraica", + author: "M.H. Alberto", + affiliation: "Departamento de Matemáticas Aplicadas", + email: "mario_alberto_claudio_h@hotmail.com", + date: "7 de julio de 2026", + abstract: [ + En este artículo se presenta una introducción rigurosa a la regresión + lineal desde una perspectiva algebraica y computacional. Se desarrolla el + método de mínimos cuadrados ordinarios, se analiza la descomposición + matricial del problema y se implementa una solución en Python. Se incluyen + demostraciones formales, ejemplos numéricos y referencias a la literatura + clásica @pearson1901. + ], + keywords: "regresión lineal, mínimos cuadrados, álgebra lineal, Python, OLS", + bib: bibliography("refs.bib", style: "ieee"), +) + += Introducción + +La regresión lineal es uno de los modelos fundamentales del análisis +estadístico y constituye la primera aproximación al estudio de relaciones +entre variables @pearson1901. Su objetivo principal es modelar la relación +entre un conjunto de variables predictoras y una variable de respuesta +mediante una función lineal. + +#axioma(titulo: "de Linealidad")[ + Existe una relación funcional $y = f(x) + epsilon$ donde $f$ es una función + lineal en los parámetros y $epsilon$ es un término de error aleatorio con + $EE[epsilon] = 0$. +] + +El Axioma @ax-linealidad establece la hipótesis fundamental sobre la que se +construye toda la teoría de regresión lineal. A partir de este principio, el +problema se reduce a encontrar los coeficientes que minimizan el error. + +== Planteamiento del Problema + +#definicion(titulo: "Regresión Lineal Simple")[ + Sea $cal(D) = {(x_i, y_i)}_(i=1)^n subset RR^2$ un conjunto de datos + observados. La regresión lineal simple consiste en encontrar coeficientes + $beta_0, beta_1 in RR$ tales que + #ecuacion[$ y_i approx beta_0 + beta_1 x_i, quad i = 1, dots, n . $] +] + +La ecuación @eq-modelo establece que cada valor observado $y_i$ se aproxima +mediante una combinación lineal de $x_i$. Dado que el sistema tiene $n$ +ecuaciones y solo $2$ incógnitas, es generalmente sobredeterminado. + +#ejemplo[ + Consideremos el siguiente conjunto de datos sobre la relación entre horas + de estudio ($x$) y calificación obtenida ($y$): + + #tabla( + columns: 2, + table.header([Horas ($x$)], [Calificación ($y$)]), + [2], [65], + [3], [72], + [5], [81], + [7], [88], + [9], [95], + caption: [Datos de ejemplo: horas de estudio vs. calificación.], + ) + + Buscamos una recta $y = beta_0 + beta_1 x$ que mejor se ajuste a estos + puntos, en el sentido de minimizar el error cuadrático total. +] + += Formulación Matricial + +== El Sistema Sobredeterminado + +El modelo lineal puede expresarse en forma matricial. Para $n$ observaciones +y $p$ variables predictoras, definimos la matriz de diseño $X in RR^(n times p)$ y el vector de respuesta $y in RR^n$: + +#ecuacion[$ y = X beta + epsilon, quad X = mat(1, x_11, dots, x_(1,p-1); 1, x_21, dots, x_(2,p-1); dots.v, dots.v, dots.down, dots.v; 1, x_(n 1), dots, x_(n,p-1)) $] + +donde $beta in RR^p$ es el vector de coeficientes y $epsilon in RR^n$ es el +vector de errores. La ecuación @eq-forma-matricial es la representación +estándar del modelo lineal general. + +#teorema(titulo: "de Mínimos Cuadrados Ordinarios")[ + Sea $X in RR^(n times p)$ una matriz de diseño de rango completo + ($op("rango")(X) = p$). El estimador de mínimos cuadrados ordinarios (OLS) + que minimiza $norm(y - X beta)^2$ está dado por + + #ecuacion[$ hat(beta) = (X^T X)^(-1) X^T y . $] +] + +#demostracion[ + La función objetivo es $S(beta) = norm(y - X beta)^2 = (y - X beta)^T (y - X beta)$. + Derivando respecto a $beta$ e igualando a cero: + + $ frac(partial S, partial beta) = -2 X^T (y - X beta) = 0 . $ + + Esto conduce a las ecuaciones normales $X^T X beta = X^T y$. Como $X$ tiene + rango completo, $X^T X$ es invertible y obtenemos la ecuación @eq-ols. +] + +#proposicion[ + La matriz $X^T X$ es simétrica y definida positiva cuando $X$ tiene rango + completo. En consecuencia, el problema de mínimos cuadrados tiene solución + única. +] + +#corolario[ + El vector de valores ajustados es $hat(y) = X hat(beta) = H y$, donde + $H = X (X^T X)^(-1) X^T$ es la matriz de proyección ortogonal sobre el + espacio columna de $X$. +] + +=== Propiedades de la Matriz de Proyección + +#lema(titulo: "Idempotencia de H")[ + La matriz de proyección $H$ es simétrica ($H^T = H$) e idempotente + ($H^2 = H$). Además, $op("tr")(H) = p$ y sus eigenvalores son $0$ o $1$. +] + +#demostracion[ + La simetría es inmediata: $H^T = (X (X^T X)^(-1) X^T)^T = X (X^T X)^(-T) X^T = H$. + Para la idempotencia: + + #ecuacion[$ H^2 = X (X^T X)^(-1) X^T X (X^T X)^(-1) X^T = X (X^T X)^(-1) X^T = H . $] + + La ecuación @eq-idempotencia confirma que $H$ proyecta sobre el mismo + subespacio al aplicarse dos veces. +] + += Métricas de Evaluación + +Una vez ajustado el modelo, es necesario evaluar su calidad. Las métricas más +comunes son el coeficiente de determinación $R^2$ y el error cuadrático medio +(RMSE). + +#definicion(titulo: "Coeficiente de Determinación")[ + El coeficiente de determinación se define como + + #ecuacion[$ R^2 = 1 - frac(sum_(i=1)^n (y_i - hat(y)_i)^2, sum_(i=1)^n (y_i - overline(y))^2) , $] + + donde $hat(y)_i$ son los valores ajustados y $overline(y)$ es la media + muestral. $R^2 in [0,1]$ mide la proporción de varianza explicada por el + modelo. +] + +La Definición @def-r2 cuantifica qué tan bien se ajusta el modelo a los datos. +Un valor de $R^2$ cercano a $1$ indica un buen ajuste. + +#problema(titulo: "Cálculo de Métricas")[ + Utilizando los datos de la Tabla @tabla-datos, calcula manualmente los + coeficientes de la recta de regresión y determina el valor de $R^2$. +] + +#solucion[ + Construimos la matriz de diseño y el vector de respuesta: + + $ X = mat(1, 2; 1, 3; 1, 5; 1, 7; 1, 9), quad y = mat(65; 72; 81; 88; 95) . $ + + Aplicando la fórmula @eq-ols obtenemos $hat(beta)_0 approx 53.47$, + $hat(beta)_1 approx 4.69$. La recta ajustada es $y approx 53.47 + 4.69 x$. + Sustituyendo en @eq-r2 se obtiene $R^2 approx 0.991$. +] + += Implementación en Python + +A continuación se presenta una implementación del estimador OLS en Python +utilizando las bibliotecas `numpy` y `pandas`. + +== Carga y Preparación de Datos + +#codigo-formal(``` +import numpy as np +import pandas as pd + +datos = pd.DataFrame({ + "horas": [2, 3, 5, 7, 9], + "calificacion": [65, 72, 81, 88, 95] +}) + +X = datos[["horas"]].values +y = datos["calificacion"].values +print("X:", X.shape, "y:", y.shape) +```) + +== Implementación del Estimador OLS + +#codigo-formal(titulo: "Estimador de Mínimos Cuadrados", ``` +def ols_estimador(X, y): + n, p = X.shape + X_d = np.hstack([np.ones((n, 1)), X]) + XtX = X_d.T @ X_d + Xty = X_d.T @ y + beta = np.linalg.solve(XtX, Xty) + y_pred = X_d @ beta + residuos = y - y_pred + ss_res = (residuos * residuos).sum() + ss_tot = ((y - y.mean()) * (y - y.mean())).sum() + r2 = 1 - ss_res / ss_tot + return beta, y_pred, r2 + +beta, y_pred, r2 = ols_estimador(X, y) +print("beta:", beta) +print("R2:", round(r2, 4)) +```) + +El Listado @listado-ols implementa directamente la fórmula del +Teorema @teo-ols, utilizando la descomposición de la matriz $X^T X$ para +resolver las ecuaciones normales de forma numéricamente estable. + += Resultados Experimentales + +== Comparación de Métodos + +Evaluamos el modelo OLS contra una regresión Ridge con distintos valores del +parámetro de regularización $lambda$. + +#tabla( + columns: 4, + table.header([Método], [$lambda$], [RMSE], [$R^2$]), + [OLS], [---], [2.341], [0.991], + [Ridge], [0.1], [2.389], [0.988], + [Ridge], [1.0], [2.521], [0.979], + [Ridge], [10.0], [3.107], [0.951], + caption: [Comparación de OLS y regresión Ridge para los datos de + horas de estudio vs. calificación.], +) + +La Tabla @tabla-comparacion muestra que OLS ofrece el mejor ajuste en +términos de $R^2$, como era de esperar al ser el estimador insesgado de +mínima varianza bajo los supuestos clásicos. + +== Visualización del Ajuste + +#figura( + rect(width: 100%, height: 5cm, fill: rgb("#f1f5f9"), stroke: 0.5pt, inset: 12pt)[ + *[Recta de regresión ajustada]* + #v(1em) + Gráfica de dispersión de los datos con la recta $y = 53.47 + 4.69x$ + superpuesta. Los residuos se muestran como líneas verticales. + ], + caption: [Ajuste por mínimos cuadrados de los datos de la + Tabla @tabla-datos.], +) + +#figura( + image("cuadrado.png", width: 40%), + caption: [Representación gráfica del método de mínimos cuadrados + @pearson1901.], +) + += Conclusiones + +La regresión lineal, fundamentada en el álgebra lineal y la optimización, +proporciona un marco teórico sólido para el modelado estadístico. El +Teorema @teo-ols, demostrado formalmente, establece la solución óptima +para el problema de mínimos cuadrados. Las propiedades de la matriz de +proyección analizadas en el Lema @lema-idempotente son esenciales para +comprender la geometría del ajuste. La Definición @def-r2 proporciona una +métrica estándar para evaluar la calidad predictiva del modelo. + +La implementación computacional en Python (Listado @listado-ols) muestra +cómo los conceptos teóricos se traducen directamente en código funcional, +cerrando el ciclo entre las matemáticas y la práctica del análisis de datos. diff --git a/packages/preview/tsiib/0.1.0/refs.bib b/packages/preview/tsiib/0.1.0/refs.bib new file mode 100644 index 0000000000..d79842655d --- /dev/null +++ b/packages/preview/tsiib/0.1.0/refs.bib @@ -0,0 +1,24 @@ +@article{bellman1961, + title = {On the curse of dimensionality}, + author = {Bellman, Richard}, + journal = {Adaptive Control Processes: A Guided Tour}, + year = {1961}, +} + +@article{pearson1901, + title = {On lines and planes of closest fit to systems of points in space}, + author = {Pearson, Karl}, + journal = {The London, Edinburgh, and Dublin Philosophical Magazine and Journal of Science}, + volume = {2}, + number = {11}, + pages = {559--572}, + year = {1901}, +} + +@book{chollet2021, + title = {Deep Learning with Python}, + author = {Chollet, François}, + year = {2021}, + publisher = {Manning Publications}, + edition = {2}, +} diff --git a/packages/preview/tsiib/0.1.0/src/codigo.typ b/packages/preview/tsiib/0.1.0/src/codigo.typ new file mode 100644 index 0000000000..fa5bb5b9a2 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/src/codigo.typ @@ -0,0 +1,45 @@ +// ========================================== +// BLOQUES DE CÓDIGO FUENTE ACADÉMICO +// ========================================== + +#import "counters.typ": numbering-seccion + +#let codigo-formal(codigo, lang: "python", titulo: none) = { + let texto = if type(codigo) == str { codigo } else { codigo.text } + + let bloque = block( + width: 100%, + fill: luma(250), + stroke: 0.5pt + luma(200), + inset: (x: 1em, y: 1em), + radius: 2pt, + breakable: true, + [ + #set align(left) + #set text(size: 10pt) + + #show raw.line: it => { + box( + width: 1.5em, + align(right, text(fill: luma(150), size: 8pt)[#it.number]) + ) + h(1em) + it.body + } + + #raw(texto, block: true, lang: lang) + ] + ) + + if titulo != none { + figure( + bloque, + kind: "code", + supplement: [Listado], + numbering: numbering-seccion, + caption: titulo, + ) + } else { + bloque + } +} diff --git a/packages/preview/tsiib/0.1.0/src/counters.typ b/packages/preview/tsiib/0.1.0/src/counters.typ new file mode 100644 index 0000000000..c24d1512a4 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/src/counters.typ @@ -0,0 +1,28 @@ +// ========================================== +// UTILIDADES DE CONTADORES Y PREFIJOS DE SECCIÓN +// Numeración homogénea: sección principal (1.x) +// ========================================== + +// Numbering para figures/tablas/código +#let numbering-seccion = n => { + context { + let h = counter(heading).get() + if h.len() > 0 and h.at(0) > 0 { + str(h.at(0)) + "." + str(n) + } else { + str(n) + } + } +} + +// Numbering para ecuaciones +#let numbering-ecuacion = n => { + context { + let h = counter(heading).get() + if h.len() > 0 and h.at(0) > 0 { + "(" + str(h.at(0)) + "." + str(n) + ")" + } else { + "(" + str(n) + ")" + } + } +} diff --git a/packages/preview/tsiib/0.1.0/src/figures.typ b/packages/preview/tsiib/0.1.0/src/figures.typ new file mode 100644 index 0000000000..f843af7647 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/src/figures.typ @@ -0,0 +1,73 @@ +// ========================================== +// FIGURAS, TABLAS E IMÁGENES +// ========================================== + +#import "counters.typ": numbering-seccion + +// Figura auto-numerada con caption abajo +#let figura(body, caption: none) = { + figure( + { align(center, body) }, + kind: "image", + supplement: [Figura], + numbering: numbering-seccion, + caption: caption, + ) +} + +// Tabla estilo booktabs (LaTeX profesional) +#let tabla(caption: none, columns: auto, ..body) = { + figure( + { + set table( + stroke: none, + inset: (x: 10pt, y: 6pt), + align: horizon, + ) + table( + columns: columns, + table.hline(stroke: 1.2pt), + ..body, + table.hline(stroke: 1.2pt), + ) + }, + kind: "table", + supplement: [Tabla], + numbering: numbering-seccion, + caption: caption, + ) +} + +// Atajo para insertar imágenes desde archivo +#let imagen(path, width: 100%, caption: none) = { + figura( + image(path, width: width), + caption: caption, + ) +} + +// Imagen a la izquierda, texto a la derecha +#let figura-izq-texto(ruta, caption: none, img-width: 40%, gutter: 5%, texto) = { + block(width: 100%, below: 1.5em)[ + #grid( + columns: (img-width, 1fr), + gutter: gutter, + align: top, + figure(image(ruta, width: 100%), kind: "image", supplement: [Figura], numbering: numbering-seccion, caption: caption), + texto, + ) + ] +} + +// Imagen a la derecha, texto a la izquierda +#let figura-der-texto(ruta, caption: none, img-width: 40%, gutter: 5%, texto) = { + block(width: 100%, below: 1.5em)[ + #grid( + columns: (1fr, img-width), + gutter: gutter, + align: top, + texto, + figure(image(ruta, width: 100%), kind: "image", supplement: [Figura], numbering: numbering-seccion, caption: caption), + ) + ] +} diff --git a/packages/preview/tsiib/0.1.0/src/links.typ b/packages/preview/tsiib/0.1.0/src/links.typ new file mode 100644 index 0000000000..e947751b50 --- /dev/null +++ b/packages/preview/tsiib/0.1.0/src/links.typ @@ -0,0 +1,8 @@ +// ========================================== +// CONFIGURACIÓN DE HIPERVÍNCULOS EN COLOR AZUL +// ========================================== + +#let configurar-links() = { + show link: it => link(it.dest, text(fill: rgb("#2563eb"))[#it.body]) + show cite: it => text(fill: rgb("#2563eb"))[#it] +} diff --git a/packages/preview/tsiib/0.1.0/src/macros.typ b/packages/preview/tsiib/0.1.0/src/macros.typ new file mode 100644 index 0000000000..24b7f0821e --- /dev/null +++ b/packages/preview/tsiib/0.1.0/src/macros.typ @@ -0,0 +1,76 @@ +// ========================================== +// ENTORNOS MATEMÁTICOS +// ========================================== + +#import "counters.typ": numbering-ecuacion + +// Fábrica de entornos numerados — contadores planos sin context anidado +#let entorno-numerado(kind, nombre, cuerpo, titulo: none) = { + figure( + block( + width: 100%, + above: 1.5em, + below: 1.5em, + breakable: true, + { + set align(left) + set par(justify: true, leading: 0.6em, first-line-indent: 1.5em) + context { + let n = counter(figure.where(kind: kind)).get().at(0) + let h = counter(heading).get() + let sec = if h.len() > 0 and h.at(0) > 0 { str(h.at(0)) } else { none } + let num = if sec != none { sec + "." + str(n) } else { str(n) } + if titulo != none { + [ #text(weight: "bold")[#nombre #num (#titulo).] #cuerpo ] + } else { + [ #text(weight: "bold")[#nombre #num.] #cuerpo ] + } + } + } + ), + kind: kind, + supplement: [#nombre], + numbering: "1", + caption: none, + placement: none, + outlined: false, + ) +} + +// Entorno no numerado genérico +#let entorno-no-numerado(nombre, cuerpo, qed: false) = { + block( + width: 100%, + above: 1.5em, + below: 1.5em, + breakable: true, + { + set align(left) + set par(justify: true, leading: 0.6em, first-line-indent: 1.5em) + if qed { + [*#nombre.* #cuerpo #h(1fr) $square$] + } else { + [*#nombre.* #cuerpo] + } + } + ) +} + +// ── Macros públicas ── + +#let teorema(titulo: none, cuerpo) = entorno-numerado("teo", "Teorema", cuerpo, titulo: titulo) +#let proposicion(titulo: none, cuerpo) = entorno-numerado("teo", "Proposición", cuerpo, titulo: titulo) +#let corolario(titulo: none, cuerpo) = entorno-numerado("teo", "Corolario", cuerpo, titulo: titulo) +#let axioma(titulo: none, cuerpo) = entorno-numerado("teo", "Axioma", cuerpo, titulo: titulo) + +#let problema(titulo: none, cuerpo) = entorno-numerado("prob", "Problema", cuerpo, titulo: titulo) +#let ejemplo(titulo: none, cuerpo) = entorno-numerado("ej", "Ejemplo", cuerpo, titulo: titulo) +#let definicion(titulo: none, cuerpo) = entorno-numerado("def", "Definición", cuerpo, titulo: titulo) +#let lema(titulo: none, cuerpo) = entorno-numerado("lema", "Lema", cuerpo, titulo: titulo) + +#let demostracion(cuerpo) = entorno-no-numerado("Demostración", cuerpo, qed: true) +#let solucion(cuerpo) = entorno-no-numerado("Solución", cuerpo) + +#let ecuacion(cuerpo) = { + math.equation(block: true, numbering: numbering-ecuacion, cuerpo) +} diff --git a/packages/preview/tsiib/0.1.0/src/template.typ b/packages/preview/tsiib/0.1.0/src/template.typ new file mode 100644 index 0000000000..5ca8eb5a1c --- /dev/null +++ b/packages/preview/tsiib/0.1.0/src/template.typ @@ -0,0 +1,165 @@ +// ========================================== +// PLANTILLA PRINCIPAL — FUNCIÓN tsiib() +// ========================================== + +#import "links.typ": configurar-links + +#let kinds-ref = ("teo", "prob", "ej", "def", "lema", "image", "table", "code") + +#let tsiib( + title: "Título del Documento", + subtitle: none, + author: none, + affiliation: none, + email: none, + date: datetime.today().display("[day] de [month repr:long] de [year]"), + abstract: none, + keywords: none, + bib: none, + body +) = { + { + set document(title: title, author: author) + if keywords != none { + set document(keywords: keywords) + } + } + + set page( + paper: "us-letter", + margin: (x: 3cm, y: 3cm), + header: context { + if counter(page).get().at(0) > 1 { + align(right)[#text(9pt, style: "italic", fill: luma(0))[#title]] + } + }, + footer: context { + if counter(page).get().at(0) > 1 { + align(center)[#text(9pt, fill: luma(0))[#counter(page).get().at(0)]] + } + }, + ) + + set text( + font: "New Computer Modern", + size: 11pt, + lang: "es", + ) + + show math.equation: set text(font: "New Computer Modern Math") + + set par(justify: true, leading: 0.65em, first-line-indent: 1.5em) + + set heading(numbering: "1.1") + + show heading: set par(first-line-indent: 0pt) + show heading: it => block(above: 2.5em, below: 1.5em)[#it] + + show heading.where(level: 1): it => { + counter(figure.where(kind: "teo")).update(0) + counter(figure.where(kind: "prob")).update(0) + counter(figure.where(kind: "ej")).update(0) + counter(figure.where(kind: "def")).update(0) + counter(figure.where(kind: "lema")).update(0) + counter(figure.where(kind: "image")).update(0) + counter(figure.where(kind: "table")).update(0) + counter(figure.where(kind: "code")).update(0) + counter(math.equation).update(0) + text(size: 14pt, weight: "bold")[#it] + } + + show heading.where(level: 2): it => { + text(size: 12pt, weight: "bold")[#it] + } + + show heading.where(level: 3): it => { + text(size: 11pt, weight: "bold")[#it] + } + + // Links azules (cites y links; el ref se maneja abajo) + configurar-links() + + // Referencias con jerarquía completa de headings + color azul + show ref: it => { + let el = it.element + if el != none and el.func() == figure and el.kind in kinds-ref { + let k = el.kind + let sup = el.supplement + text(fill: rgb("#2563eb"))[ + #link(el.location())[#sup #context { + let h = counter(heading).at(el.location()) + let n = counter(figure.where(kind: k)).at(el.location()).first() + if h.len() > 0 and h.at(0) > 0 { + str(h.at(0)) + "." + str(n) + } else { + str(n) + } + }] + ] + } else { + text(fill: rgb("#2563eb"))[#it] + } + } + + // Entornos: override centrado de figure + let kinds = ("teo", "prob", "ej", "def", "lema") + for k in kinds { + show figure.where(kind: k): it => align(left, it.body) + } + + // ── Portada ── + align(center)[ + #v(3em) + #block(text(weight: "bold", size: 2.2em)[#title]) + #if subtitle != none { + v(1em) + block(text(weight: "medium", size: 1.4em)[#subtitle]) + } + #v(4em) + #if author != none { block(text(size: 1.2em)[#author]) } + #if affiliation != none { + v(0.2em) + block(text(size: 1.1em)[#affiliation]) + } + #if email != none { + v(0.2em) + link("mailto:" + email) + } + #if date != none { + v(2em) + block(text(size: 1.1em)[#date]) + } + ] + + v(4em) + if abstract != none { + align(center)[ + #block(width: 85%)[ + *Resumen* \ + #v(0.8em) + #align(left)[#abstract] + ] + ] + v(3em) + } + + // Índice en azul + set text(fill: rgb("#2563eb")) + outline( + title: [Índice], + indent: auto, + depth: 3, + ) + set text(fill: black) + + pagebreak(weak: true) + + body + + // ── Bibliografía ── + if bib != none { + pagebreak(weak: true) + set heading(numbering: none) + bib + } +} diff --git a/packages/preview/tsiib/0.1.0/typst.toml b/packages/preview/tsiib/0.1.0/typst.toml new file mode 100644 index 0000000000..dfe8d2cc0e --- /dev/null +++ b/packages/preview/tsiib/0.1.0/typst.toml @@ -0,0 +1,12 @@ +[package] +name = "tsiib" +version = "0.1.0" +entrypoint = "lib.typ" +authors = ["M.H. Alberto"] +license = "MIT" +description = "Plantilla tipográfica para artículos académicos en español con estilo LaTeX." +repository = "https://github.com/MHAlberto/tsiib" +keywords = ["article", "spanish", "math", "latex", "academic"] +categories = ["paper"] +compiler = "0.15.0" +exclude = ["/main.typ", "/refs.bib", "/cuadrado.png"]