@jossuealcala/madre 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/public/app.js +158 -20
- package/public/es.js +210 -2
- package/public/index.html +2 -2
- package/public/styles.css +20 -3
- package/public/troubleshooting.js +87 -0
- package/src/adapters/process.mjs +62 -15
- package/src/mcp/image-server.mjs +24 -2
- package/src/modules/image-studio.mjs +3 -2
- package/src/modules/playwright.mjs +9 -5
- package/src/outbound.mjs +6 -1
- package/src/sentinel-errors.mjs +19 -1
- package/src/server.mjs +14 -6
package/public/es.js
CHANGED
|
@@ -295,8 +295,9 @@ export const ES = {
|
|
|
295
295
|
'an entry in ~/.pulse/config.json': 'una entrada en ~/.pulse/config.json',
|
|
296
296
|
'an MCP server per turn, started and stopped by the room':
|
|
297
297
|
'un servidor MCP por turno, que la sala levanta y apaga',
|
|
298
|
+
// Bajo NECESITA es un requisito, no una acción: «necesita: se engancha» no concuerda.
|
|
298
299
|
'attached only inside a creation lease with the image scope on':
|
|
299
|
-
'
|
|
300
|
+
'un permiso de creación con el alcance de imagen encendido; solo ahí se engancha',
|
|
300
301
|
'a Gemini API key with credits (the one the Gemini CLI stores, or GEMINI_API_KEY)':
|
|
301
302
|
'una API key de Gemini con créditos (la que guarda el CLI de Gemini, o GEMINI_API_KEY)',
|
|
302
303
|
|
|
@@ -1758,7 +1759,214 @@ export const ES = {
|
|
|
1758
1759
|
'DENIED · NO ANSWER IN TIME': 'NEGADO · NO HUBO RESPUESTA A TIEMPO',
|
|
1759
1760
|
'PLAN STOPPED': 'PLAN DETENIDO',
|
|
1760
1761
|
'DENIED': 'NEGADO',
|
|
1761
|
-
'created · {n} files': 'creados · {n}
|
|
1762
|
+
'created · {n} {files}': 'creados · {n} {files}',
|
|
1763
|
+
'file': 'archivo',
|
|
1764
|
+
'files': 'archivos',
|
|
1765
|
+
// La severidad de una condición de MU/TH/UR. En mayúsculas porque así se lee la ficha, y
|
|
1766
|
+
// porque así no choca con 'warning' en minúsculas, que es el nivel de una cuota.
|
|
1767
|
+
'BLOCKING': 'BLOQUEANTE',
|
|
1768
|
+
'COMMON': 'COMÚN',
|
|
1769
|
+
'DEGRADED': 'DEGRADADA',
|
|
1770
|
+
'ALREADY FIXED': 'RESUELTA',
|
|
1771
|
+
'INFORMATIONAL': 'INFORMATIVA',
|
|
1772
|
+
'TRANSIENT': 'PASAJERA',
|
|
1773
|
+
'TUNABLE': 'AJUSTABLE',
|
|
1774
|
+
'WARNING': 'AVISO',
|
|
1775
|
+
'a note its own sources do not support': 'una nota que sus propias fuentes no sostienen',
|
|
1776
|
+
|
|
1777
|
+
// ── DOS CONDICIONES QUE REPORTÓ QUIEN LA USA ──────────────────────────────
|
|
1778
|
+
'ANTHROPIC_API_KEY takes precedence over the claude.ai plan':
|
|
1779
|
+
'ANTHROPIC_API_KEY gana sobre el plan de claude.ai',
|
|
1780
|
+
'Claude Code found an API key in the environment as well as the claude.ai login, and the key wins. MADRE hands the CLI the environment exactly as this machine has it, so whatever is exported in your shell profile reaches it. The turn is billed to the API account, not to the plan, and your organisation connectors do not load.':
|
|
1781
|
+
'Claude Code encontró en el entorno una llave de API además del inicio de sesión de claude.ai, y gana la llave. MADRE le entrega al CLI el entorno tal como lo tiene esta computadora, así que lo que esté exportado en el perfil de tu shell le llega. El turno se le cobra a la cuenta de API, no al plan, y los conectores de tu organización no cargan.',
|
|
1782
|
+
'Decide which one you mean. To work on the plan, unset the key where your shell exports it and open the room again; to work on the API, this message is only a notice and can be ignored.':
|
|
1783
|
+
'Decide cuál quieres. Para trabajar con el plan, quita la llave de donde tu shell la exporta y vuelve a abrir la sala; para trabajar con la API, este mensaje es solo un aviso y se puede ignorar.',
|
|
1784
|
+
// La condición que declara el módulo PLAYWRIGHT. Las de los módulos viven en su archivo, no
|
|
1785
|
+
// en troubleshooting.js, y por eso se quedaron fuera del catálogo hasta ahora.
|
|
1786
|
+
'PLAYWRIGHT: the browser server is not installed': 'PLAYWRIGHT: el servidor de navegador no está instalado',
|
|
1787
|
+
'The PLAYWRIGHT module runs @playwright/mcp per turn. Two different things have to be on this computer and only one of them is the package: the server, and the browser build that this version of it expects. A server that starts without its browser answers every tool call with "Browser … is not installed", and one that cannot start at all reaches the agent as CONNECTION_CLOSED.':
|
|
1788
|
+
'El módulo PLAYWRIGHT corre @playwright/mcp en cada turno. Tienen que estar en esta computadora dos cosas distintas y solo una es el paquete: el servidor, y la versión de navegador que esa versión del servidor espera. Un servidor que arranca sin su navegador contesta a cada herramienta con «Browser … is not installed», y uno que no logra arrancar le llega al agente como CONNECTION_CLOSED.',
|
|
1789
|
+
'Install the server once, globally, then its browser — the browser is downloaded by the server itself, not by the playwright CLI — and RECHECK in MODULES.':
|
|
1790
|
+
'Instala el servidor una vez, global, y después su navegador —lo baja el servidor mismo, no el CLI de playwright— y dale RE-REVISAR en MÓDULOS.',
|
|
1791
|
+
// El registro de errores: un código que se dicta por teléfono, y una salida por fila.
|
|
1792
|
+
'SHOW THE {n} DISMISSED': 'VER LOS {n} DESCARTADOS',
|
|
1793
|
+
'DISMISS ALL {n}': 'DESCARTAR LOS {n}',
|
|
1794
|
+
'DOWNLOAD THE LOG': 'DESCARGAR EL REGISTRO',
|
|
1795
|
+
'OR ON MADRE.RUN ↗': 'O EN MADRE.RUN ↗',
|
|
1796
|
+
'✎ REPORT ON MADRE.RUN ↗': '✎ REPORTAR EN MADRE.RUN ↗',
|
|
1797
|
+
'OPEN ⚙ CONNECTIONS · @{agent}': 'ABRIR ⚙ CONEXIONES · @{agent}',
|
|
1798
|
+
'OPEN ⚙ CONNECTIONS': 'ABRIR ⚙ CONEXIONES',
|
|
1799
|
+
// ── LA PROSA DENTRO DE LOS REMEDIOS ──────────────────────────────────────
|
|
1800
|
+
// Un comando no se traduce nunca. Un comentario sí: es prosa que alguien lee, y la
|
|
1801
|
+
// mitad nombra botones que en esta sala se llaman de otra manera.
|
|
1802
|
+
'# remove or comment the line it finds, then:':
|
|
1803
|
+
'# borra o comenta la línea que encuentre, y luego:',
|
|
1804
|
+
'# and start MADRE from a new terminal':
|
|
1805
|
+
'# y arranca MADRE desde una terminal nueva',
|
|
1806
|
+
'# inside gemini: /auth → "Use Gemini API key" and paste the key from https://aistudio.google.com/app/apikey':
|
|
1807
|
+
'# dentro de gemini: /auth → "Use Gemini API key" y pega la llave de https://aistudio.google.com/app/apikey',
|
|
1808
|
+
'# or, without the prompt:':
|
|
1809
|
+
'# o, sin el diálogo:',
|
|
1810
|
+
'# persist: echo \'export GEMINI_API_KEY="YOUR_KEY"\' >> ~/.zshrc':
|
|
1811
|
+
'# para que quede: echo \'export GEMINI_API_KEY="TU_LLAVE"\' >> ~/.zshrc',
|
|
1812
|
+
'# inside gemini: /auth → "Use Gemini API key"':
|
|
1813
|
+
'# dentro de gemini: /auth → "Use Gemini API key"',
|
|
1814
|
+
'# persist: echo \'export GEMINI_API_KEY="YOUR_KEY"\' >> ~/.bashrc':
|
|
1815
|
+
'# para que quede: echo \'export GEMINI_API_KEY="TU_LLAVE"\' >> ~/.bashrc',
|
|
1816
|
+
'# billing: https://ai.studio/projects':
|
|
1817
|
+
'# facturación: https://ai.studio/projects',
|
|
1818
|
+
'# check the key still works:':
|
|
1819
|
+
'# comprueba que la llave siga sirviendo:',
|
|
1820
|
+
'# in the room: click the Gemini sphere twice → choose gemini-3-flash-preview':
|
|
1821
|
+
'# en la sala: doble clic en la esfera de Gemini → elige gemini-3-flash-preview',
|
|
1822
|
+
'# check quota: https://aistudio.google.com/app/apikey':
|
|
1823
|
+
'# revisa la cuota: https://aistudio.google.com/app/apikey',
|
|
1824
|
+
'# quota: https://aistudio.google.com/app/apikey':
|
|
1825
|
+
'# cuota: https://aistudio.google.com/app/apikey',
|
|
1826
|
+
'# wait, then resend — or continue with @codex / @claude / @opencode':
|
|
1827
|
+
'# espera y reenvía — o sigue con @codex / @claude / @opencode',
|
|
1828
|
+
'# press [m] and pick a provider/model that has a session, e.g. openai/gpt-5.6-sol':
|
|
1829
|
+
'# presiona [m] y elige un proveedor/modelo con sesión, por ejemplo openai/gpt-5.6-sol',
|
|
1830
|
+
'# one-off alternative:':
|
|
1831
|
+
'# alternativa de una sola vez:',
|
|
1832
|
+
'# or drop the stale key:':
|
|
1833
|
+
'# o tira la llave vencida:',
|
|
1834
|
+
'# press [m] and pick a provider/model that has a session':
|
|
1835
|
+
'# presiona [m] y elige un proveedor/modelo con sesión',
|
|
1836
|
+
'# or directly:':
|
|
1837
|
+
'# o directo:',
|
|
1838
|
+
'# pick the agent you want; see per-agent commands below':
|
|
1839
|
+
'# elige el agente que quieras; abajo están los comandos de cada uno',
|
|
1840
|
+
'# live, no restart: ⚙ CONNECTIONS → DEFAULT TIMEOUT · SECONDS → SAVE':
|
|
1841
|
+
'# en vivo, sin reiniciar: ⚙ CONEXIONES → TIEMPO LÍMITE POR DEFECTO · SEGUNDOS → GUARDAR',
|
|
1842
|
+
'# at launch only (env wins over config.json):':
|
|
1843
|
+
'# solo al arrancar (el entorno gana sobre config.json):',
|
|
1844
|
+
'# or ~/.pulse/config.json → {"timeouts":{"default":300000,"claude":600000}}':
|
|
1845
|
+
'# o ~/.pulse/config.json → {"timeouts":{"default":300000,"claude":600000}}',
|
|
1846
|
+
'# resend the question':
|
|
1847
|
+
'# reenvía la pregunta',
|
|
1848
|
+
'# click the sphere → 5h / 7d windows and reset times':
|
|
1849
|
+
'# clic en la esfera → ventanas de 5 h / 7 d y a qué hora se reinician',
|
|
1850
|
+
'# live: ⚙ CONNECTIONS → LOCAL TOKEN BUDGET PER AGENT → SAVE':
|
|
1851
|
+
'# en vivo: ⚙ CONEXIONES → PRESUPUESTO LOCAL DE TOKENS POR AGENTE → GUARDAR',
|
|
1852
|
+
'# at launch: PULSE_SOFT_TOKEN_BUDGET=1000000 madre start':
|
|
1853
|
+
'# al arrancar: PULSE_SOFT_TOKEN_BUDGET=1000000 madre start',
|
|
1854
|
+
'# or ~/.pulse/config.json → {"room":{"softTokenBudget":1000000}}':
|
|
1855
|
+
'# o ~/.pulse/config.json → {"room":{"softTokenBudget":1000000}}',
|
|
1856
|
+
'# who holds 4317?':
|
|
1857
|
+
'# ¿quién tiene tomado el 4317?',
|
|
1858
|
+
'# in the room: type STOPALL, then resend':
|
|
1859
|
+
'# en la sala: escribe STOPALL y reenvía',
|
|
1860
|
+
'# in the room composer:':
|
|
1861
|
+
'# en el compositor de la sala:',
|
|
1862
|
+
'# or from a terminal:':
|
|
1863
|
+
'# o desde una terminal:',
|
|
1864
|
+
'# MODULES → Image Studio → ENABLE':
|
|
1865
|
+
'# MÓDULOS → Image Studio → ENCENDER',
|
|
1866
|
+
'# ⚙ CONNECTIONS → Gemini → GENERATE IMAGES → SAVE':
|
|
1867
|
+
'# ⚙ CONEXIONES → Gemini → GENERA IMÁGENES → GUARDAR',
|
|
1868
|
+
'# then: CREATE + "generate … as name.png"':
|
|
1869
|
+
'# después: CREATE + "genera … como nombre.png"',
|
|
1870
|
+
'# ⚙ CONNECTIONS → Claude → GENERATE IMAGES → SAVE':
|
|
1871
|
+
'# ⚙ CONEXIONES → Claude → GENERA IMÁGENES → GUARDAR',
|
|
1872
|
+
'# ⚙ CONNECTIONS → OpenCode → GENERATE IMAGES → SAVE':
|
|
1873
|
+
'# ⚙ CONEXIONES → OpenCode → GENERA IMÁGENES → GUARDAR',
|
|
1874
|
+
'# ⚙ CONNECTIONS → agent card → WEB ACCESS → SAVE':
|
|
1875
|
+
'# ⚙ CONEXIONES → ficha del agente → ACCESO WEB → GUARDAR',
|
|
1876
|
+
'# type / or @ in the field box':
|
|
1877
|
+
'# escribe / o @ en el campo',
|
|
1878
|
+
'# chip: TO @codex #1 EXCHANGE ▾ → choose':
|
|
1879
|
+
'# el chip: PARA @codex #1 EXCHANGE ▾ → elige',
|
|
1880
|
+
'# ⚙ CONNECTIONS → agent card → MAX MODE':
|
|
1881
|
+
'# ⚙ CONEXIONES → ficha del agente → MODO MÁXIMO',
|
|
1882
|
+
'# once: composer → CREATE (lock) → send, or /create <request>':
|
|
1883
|
+
'# una vez: compositor → CREATE (candado) → enviar, o /create <petición>',
|
|
1884
|
+
'# always: ⚙ CONNECTIONS → agent card → DEFAULT MODE → #2':
|
|
1885
|
+
'# siempre: ⚙ CONEXIONES → ficha del agente → MODO POR DEFECTO → #2',
|
|
1886
|
+
'# ⚙ CONNECTIONS → agent card → CREATE FILES → SAVE':
|
|
1887
|
+
'# ⚙ CONEXIONES → ficha del agente → CREAR ARCHIVOS → GUARDAR',
|
|
1888
|
+
'# composer → CREATE (lock) → send':
|
|
1889
|
+
'# compositor → CREATE (candado) → enviar',
|
|
1890
|
+
'# or: nvm install --lts':
|
|
1891
|
+
'# o: nvm install --lts',
|
|
1892
|
+
'# or your distro package for Node ≥ 22.5':
|
|
1893
|
+
'# o el paquete de tu distribución para Node ≥ 22.5',
|
|
1894
|
+
'# more recall per turn':
|
|
1895
|
+
'# más recuerdo por turno',
|
|
1896
|
+
'# persist: echo \'export PULSE_RECALL_SHARE="0.45"\' >> ~/.zshrc':
|
|
1897
|
+
'# para que quede: echo \'export PULSE_RECALL_SHARE="0.45"\' >> ~/.zshrc',
|
|
1898
|
+
'# off':
|
|
1899
|
+
'# apagado',
|
|
1900
|
+
'# persist: echo \'export PULSE_DISTILL_AGENT="claude"\' >> ~/.zshrc':
|
|
1901
|
+
'# para que quede: echo \'export PULSE_DISTILL_AGENT="claude"\' >> ~/.zshrc',
|
|
1902
|
+
'# persist: echo \'export PULSE_DISTILL_EVERY="6"\' >> ~/.zshrc':
|
|
1903
|
+
'# para que quede: echo \'export PULSE_DISTILL_EVERY="6"\' >> ~/.zshrc',
|
|
1904
|
+
'# then: MU/TH/UR → ◉ NOSTROMO':
|
|
1905
|
+
'# después: MU/TH/UR → ◉ NOSTROMO',
|
|
1906
|
+
'# MU/TH/UR → ◉ NOSTROMO → designation = project folder name':
|
|
1907
|
+
'# MU/TH/UR → ◉ NOSTROMO → la designación es el nombre de la carpeta del proyecto',
|
|
1908
|
+
'# MODULES → RIPLEY → ENABLE RIPLEY':
|
|
1909
|
+
'# MÓDULOS → RIPLEY → ENCENDER RIPLEY',
|
|
1910
|
+
'# viewer → PREVIEW / SOURCE':
|
|
1911
|
+
'# el visor → PREVIEW / SOURCE',
|
|
1912
|
+
'# MADRE does this for you; these are the alternatives:':
|
|
1913
|
+
'# MADRE hace esto por ti; estas son las alternativas:',
|
|
1914
|
+
'# ⚙ CONNECTIONS → PRIVACY → one term per line → PURGE ROOM':
|
|
1915
|
+
'# ⚙ CONEXIONES → PRIVACIDAD → un término por línea → PURGAR LA SALA',
|
|
1916
|
+
'# room → UNDO under the CONTROL notice':
|
|
1917
|
+
'# la sala → DESHACER debajo del aviso de CONTROL',
|
|
1918
|
+
'# room → the handle under the bar, on the left → NEW CONVERSATION':
|
|
1919
|
+
'# la sala → la manija bajo la barra, a la izquierda → NUEVA CONVERSACIÓN',
|
|
1920
|
+
'# the address of the open room is in the refusal itself':
|
|
1921
|
+
'# la dirección de la sala abierta viene en el propio rechazo',
|
|
1922
|
+
'# room → ◉ NOSTROMO → the dark bodies are aberrations':
|
|
1923
|
+
'# la sala → ◉ NOSTROMO → los cuerpos oscuros son aberraciones',
|
|
1924
|
+
'# ⚙ CONNECTIONS → MEMORY → THE THREE TESTS → CONSISTENCY':
|
|
1925
|
+
'# ⚙ CONEXIONES → MEMORIA → LAS TRES PRUEBAS → CONSISTENCIA',
|
|
1926
|
+
'# ⚙ CONNECTIONS → MEMORY → CARRY WHAT A MEMORY KEEPS ARRIVING WITH':
|
|
1927
|
+
'# ⚙ CONEXIONES → MEMORIA → LLEVAR AQUELLO CON LO QUE UNA MEMORIA SIEMPRE LLEGA',
|
|
1928
|
+
'# room → ◉ NOSTROMO → COLD':
|
|
1929
|
+
'# la sala → ◉ NOSTROMO → FRÍAS',
|
|
1930
|
+
'# room → ◉ NOSTROMO → ASK → PUT IN THE COMPOSER':
|
|
1931
|
+
'# la sala → ◉ NOSTROMO → PREGUNTAR → PONER EN EL COMPOSITOR',
|
|
1932
|
+
'# ⚙ CONNECTIONS → MEMORY → THE THREE TESTS':
|
|
1933
|
+
'# ⚙ CONEXIONES → MEMORIA → LAS TRES PRUEBAS',
|
|
1934
|
+
'# MATCH needs Ollama running with a chat model, and embeddings on':
|
|
1935
|
+
'# COINCIDENCIA necesita Ollama corriendo con un modelo de chat, y los embeddings encendidos',
|
|
1936
|
+
'# or: brew install node # a Homebrew node owns its own prefix':
|
|
1937
|
+
'# o: brew install node # un node de Homebrew es dueño de su propio prefijo',
|
|
1938
|
+
'# or use nvm, which owns the prefix it installs into':
|
|
1939
|
+
'# o usa nvm, que es dueño del prefijo donde instala',
|
|
1940
|
+
'# MODULES → + ADD A MODULE':
|
|
1941
|
+
'# MÓDULOS → + AGREGAR UN MÓDULO',
|
|
1942
|
+
'# docs/SDK.md is the whole contract, and docs/sdk/hello-module.mjs is one that works':
|
|
1943
|
+
'# docs/SDK.md es el contrato entero, y docs/sdk/hello-module.mjs es uno que funciona',
|
|
1944
|
+
'# the line in the room: local · @madre → CHECK IT AGAINST THIS ROOM':
|
|
1945
|
+
'# la línea en la sala: local · @madre → MEDIRLO CONTRA ESTA SALA',
|
|
1946
|
+
'# or: NOSTROMO → THE THREE TESTS → the third one':
|
|
1947
|
+
'# o: NOSTROMO → LAS TRES PRUEBAS → la tercera',
|
|
1948
|
+
'# it needs embeddings on and some turns answered by a CLI agent, which is what it compares against':
|
|
1949
|
+
'# necesita los embeddings encendidos y algunos turnos contestados por un agente de CLI, que es contra lo que compara',
|
|
1950
|
+
'# the seal lifts on its own; MU/TH/UR › NOSTROMO asks for the name again after it does':
|
|
1951
|
+
'# el sello se levanta solo; MU/TH/UR › NOSTROMO vuelve a pedir el nombre cuando pase',
|
|
1952
|
+
'# NOSTROMO → click the core → type HELP':
|
|
1953
|
+
'# NOSTROMO → clic en el núcleo → escribe HELP',
|
|
1954
|
+
'# READ MEMORIES prints the block the next turn would carry':
|
|
1955
|
+
'# LEER MEMORIAS imprime el bloque que llevaría el próximo turno',
|
|
1956
|
+
'# SPECIAL ORDER 937 is answered too':
|
|
1957
|
+
'# la ORDEN ESPECIAL 937 también se contesta',
|
|
1958
|
+
'# NOSTROMO → click the core → WHAT LEFT THIS MACHINE':
|
|
1959
|
+
'# NOSTROMO → clic en el núcleo → LO QUE SALIÓ DE ESTA MÁQUINA',
|
|
1960
|
+
'# embeddings, to keep memories on this computer: MU/TH/UR → MEMORY → EMBEDDINGS → OLLAMA':
|
|
1961
|
+
'# embeddings, para que las memorias se queden en esta computadora: MU/TH/UR → MEMORIA → EMBEDDINGS → OLLAMA',
|
|
1962
|
+
'Dismiss this record · it stays in the ledger': 'Descartar este registro · se queda en el ledger',
|
|
1963
|
+
'Codex says it is reading additional input from stdin':
|
|
1964
|
+
'Codex dice que está leyendo entrada adicional de stdin',
|
|
1965
|
+
'A notice Codex prints, not a failure. MADRE runs it with stdin closed, so the line is harmless and the turn is decided by what Codex returned. If the turn failed, the reason is the line above this one — the exit code, or a clean exit with no answer.':
|
|
1966
|
+
'Un aviso que imprime Codex, no una falla. MADRE lo corre con stdin cerrado, así que la línea es inofensiva y el turno lo decide lo que Codex haya devuelto. Si el turno falló, la razón es la línea de arriba: el código de salida, o una salida limpia sin respuesta.',
|
|
1967
|
+
'Nothing to do about the notice itself. If turns keep failing, check that this Codex is recent enough for the flags MADRE passes: `codex --version`, then update it.':
|
|
1968
|
+
'No hay nada que hacer con el aviso en sí. Si los turnos siguen fallando, revisa que este Codex sea lo bastante reciente para las banderas que MADRE le pasa: `codex --version`, y actualízalo.',
|
|
1969
|
+
'two memories that cannot both be true': 'dos memorias que no pueden ser ciertas a la vez',
|
|
1762
1970
|
'created': 'creado',
|
|
1763
1971
|
'all stop · {plans} plans, {turns} turns halted': 'alto total · {plans} planes, {turns} turnos detenidos',
|
|
1764
1972
|
'all stop · nothing was running': 'alto total · no había nada corriendo',
|
package/public/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<div class="shell">
|
|
11
11
|
<header class="bar">
|
|
12
12
|
<div class="bar-left">
|
|
13
|
-
<a class="brand" href="https://
|
|
13
|
+
<a class="brand" href="https://madre.run" target="_blank" rel="noopener noreferrer" aria-label="MADRE · madre.run" title="MADRE · madre.run">
|
|
14
14
|
<svg class="brand-mark" viewBox="0 0 72 20" aria-hidden="true">
|
|
15
15
|
<path class="beat" d="M1 10 H16 L20 10 L23.5 2 L28 18 L32 10 L35 10 L37 6 L39 10 H52" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" pathLength="100"/>
|
|
16
16
|
<circle class="beat-dot" cx="60" cy="10" r="3" fill="currentColor"/>
|
|
@@ -252,7 +252,7 @@
|
|
|
252
252
|
<button id="nostromo-recenter" class="mother-close" type="button" hidden>RECENTER</button>
|
|
253
253
|
<button id="nostromo-close" class="mother-close" type="button">LEAVE ×</button>
|
|
254
254
|
</header>
|
|
255
|
-
<canvas id="nostromo-canvas" class="nostromo-canvas" aria-label="Memories orbiting MOTHER's core"></canvas>
|
|
255
|
+
<canvas id="nostromo-canvas" class="nostromo-canvas" tabindex="-1" aria-label="Memories orbiting MOTHER's core"></canvas>
|
|
256
256
|
<p class="nostromo-help">DRAG TO MOVE · WHEEL TO ZOOM · CLICK A MEMORY · DO NOT TOUCH MOTHER</p>
|
|
257
257
|
<div id="nostromo-alert" class="nostromo-alert" role="alert" hidden>
|
|
258
258
|
<div class="klaxon"></div>
|
package/public/styles.css
CHANGED
|
@@ -313,6 +313,10 @@ dialog.nostromo::backdrop { background: #000; }
|
|
|
313
313
|
.nostromo-frame .mother-close { color: var(--ph); border-color: var(--ph-dim); }
|
|
314
314
|
.nostromo-frame .mother-close:hover { background: var(--ph-faint); }
|
|
315
315
|
.nostromo-canvas { display: block; width: 100%; height: 100%; cursor: crosshair; }
|
|
316
|
+
/* The map takes focus when NOSTROMO opens, so that Enter does not land on LEAVE. It is not in
|
|
317
|
+
the tab order (tabindex="-1"), so the ring the browser draws around it points at nothing and
|
|
318
|
+
boxes the constellation in blue. Nothing is hidden by removing it: there is nowhere to tab. */
|
|
319
|
+
.nostromo-canvas:focus, .nostromo-canvas:focus-visible { outline: none; }
|
|
316
320
|
.nostromo-canvas.over { cursor: pointer; }
|
|
317
321
|
.nostromo-canvas.dragging { cursor: grabbing; }
|
|
318
322
|
.nostromo-help { position: absolute; left: 26px; bottom: 16px; margin: 0; font-size: 9px; letter-spacing: .2em; color: var(--ph-dim); pointer-events: none; }
|
|
@@ -725,11 +729,13 @@ dialog.mother::backdrop { background: rgba(0, 0, 0, .82); backdrop-filter: blur(
|
|
|
725
729
|
.mother-card.hit p.remedy { box-shadow: 0 0 18px rgba(155, 255, 102, .18); animation: remedy-in .6s ease; }
|
|
726
730
|
@keyframes remedy-in { from { background: rgba(155, 255, 102, .35); } to { background: var(--ph-faint); } }
|
|
727
731
|
.mother-cmd { position: relative; margin: 4px 0 0; border: 1px solid var(--ph-faint); background: #000; padding: 10px 12px; }
|
|
728
|
-
.mother-cmd pre { margin: 0; font: inherit; font-size: 12px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ph); }
|
|
732
|
+
.mother-cmd pre { margin: 0; padding-right: 30px; font: inherit; font-size: 12px; line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--ph); }
|
|
729
733
|
.mother-cmd .c { color: var(--ph-dim); }
|
|
730
|
-
.mother-cmd .copy { position: absolute; top: 6px; right: 6px; border: 1px solid var(--ph-
|
|
734
|
+
.mother-cmd .copy { position: absolute; top: 6px; right: 6px; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: 1px solid var(--ph-faint); border-radius: 3px; background: #000; color: var(--ph-dim); padding: 0; cursor: pointer; }
|
|
735
|
+
.mother-cmd .copy:hover { color: var(--ph); border-color: var(--ph-dim); }
|
|
736
|
+
.mother-cmd .copy.done { color: var(--ph); border-color: var(--ph); }
|
|
731
737
|
.mother-cmd .copy:hover { color: var(--ph); border-color: var(--ph); }
|
|
732
|
-
.mother-record { display: grid; grid-template-columns: 64px 92px 1fr auto; gap:
|
|
738
|
+
.mother-record { display: grid; grid-template-columns: 64px 92px 1fr auto 20px; gap: 14px; align-items: baseline; padding: 10px 0; border-bottom: 1px solid var(--ph-faint); font-size: 12px; }
|
|
733
739
|
.mother-record .t { color: var(--ph-dim); }
|
|
734
740
|
.mother-record .a { color: var(--agent, var(--ph)); text-transform: uppercase; letter-spacing: .1em; }
|
|
735
741
|
.mother-record .e { color: rgba(210, 255, 190, .85); text-shadow: none; overflow-wrap: anywhere; }
|
|
@@ -1626,3 +1632,14 @@ dialog.viewer::backdrop { background: rgba(0,0,0,.7); backdrop-filter: blur(4px)
|
|
|
1626
1632
|
.maturity-row span { font-family: var(--mono); font-size: 9px; letter-spacing: .08em; color: var(--ph-dim); opacity: .8; }
|
|
1627
1633
|
.maturity-next { color: var(--ph); }
|
|
1628
1634
|
@media (max-width: 860px) { .maturity-row { grid-template-columns: 1fr; gap: 2px; } .maturity-row i { display: none; } }
|
|
1635
|
+
|
|
1636
|
+
/* A record you have dealt with: the way out sits at the end of its own row. */
|
|
1637
|
+
.mother-record .drop { justify-self: end; background: none; border: 0; color: var(--ph-faint); cursor: pointer; font: inherit; font-size: 14px; padding: 0; line-height: 1; }
|
|
1638
|
+
.mother-record .drop:hover { color: var(--warn); }
|
|
1639
|
+
.mother-card .tags .code { color: var(--ph); border: 1px solid var(--ph-faint); border-radius: 3px; padding: 0 4px; letter-spacing: .1em; }
|
|
1640
|
+
.mother-clear { display: inline-block; margin: 8px 10px 10px 0; background: none; border: 1px solid var(--ph-faint); border-radius: 4px; color: var(--ph-dim); cursor: pointer; font: inherit; font-size: 9px; letter-spacing: .16em; padding: 4px 10px; }
|
|
1641
|
+
.mother-clear:hover { color: var(--ph); border-color: var(--ph-dim); }
|
|
1642
|
+
|
|
1643
|
+
/* A remedy this room can carry out itself: the button goes above the commands it replaces. */
|
|
1644
|
+
.mother-goto { display: block; margin: 6px 0 0; border: 1px solid var(--ph-dim); border-radius: 4px; background: transparent; color: var(--ph); cursor: pointer; font: inherit; font-size: 10px; letter-spacing: .14em; padding: 6px 12px; text-transform: uppercase; }
|
|
1645
|
+
.mother-goto:hover { background: color-mix(in srgb, var(--ph) 12%, transparent); }
|