diff --git a/src/client/assets/styles/mobile_ux.css b/src/client/assets/styles/mobile_ux.css index a5f3afb..e3318de 100644 --- a/src/client/assets/styles/mobile_ux.css +++ b/src/client/assets/styles/mobile_ux.css @@ -593,6 +593,30 @@ body > .welcome-banner { order: -1; margin-top: 10px !important; } /* con el menu abierto, el boton de No molestar no se monta encima de la hoja */ body:has(#oasis-menu-toggle:checked) form.oasis-phone-state { display: none !important; } +/* ---------- tipo de portada: un toque y guardado ---------- */ +/* bienvenida y Ajustes: cada opcion es un boton. En el movil, una fila por opcion con + la miniatura a la izquierda (en rejilla de tres, "Conversaciones" se partia a 360px); + en tableta, rejilla de tres */ +.main-column form.ux-pick { margin: 0 !important; padding: 0 !important; border: 0 !important; background: transparent !important; box-shadow: none !important; } +.main-column .ux-pick-hint { margin: 0 0 10px !important; color: var(--ux-muted); font-size: 14px; } +.main-column .ux-pick-grid { display: block !important; border: 0 !important; background: transparent !important; padding: 0 !important; } +.main-column button.ux-pick-card { + display: flex !important; flex-direction: row; align-items: center; text-align: left; + width: 100% !important; min-width: 0 !important; min-height: 64px !important; height: auto !important; margin: 0 0 10px !important; padding: 8px 12px 8px 8px !important; + background: var(--ux-bg) !important; color: var(--ux-fg) !important; border: 2px solid var(--ux-line) !important; border-radius: 14px !important; + box-shadow: 0 3px 0 var(--ux-line) !important; text-transform: none !important; font-family: inherit !important; cursor: pointer; +} +.main-column button.ux-pick-card.is-current { background: var(--ux-accent) !important; color: var(--ux-on-accent) !important; } +.main-column .ux-pick-shot { display: block; flex: 0 0 76px; width: 76px; height: auto; border-radius: 8px; margin: 0 12px 0 0; border: 1px solid rgba(0, 0, 0, .2); } +.main-column .ux-pick-label { flex: 1 1 auto; min-width: 0; text-align: left; font-size: 16px; font-weight: 700; line-height: 1.2; } +.main-column .ux-pick-now { flex: 0 0 auto; margin-left: 8px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; } +@media (min-width: 700px) { + .main-column .ux-pick-grid { display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-gap: 12px; } + .main-column button.ux-pick-card { flex-direction: column; text-align: center; margin: 0 !important; padding: 10px !important; } + .main-column .ux-pick-shot { width: 100%; flex: 0 0 auto; margin: 0 0 8px; } + .main-column .ux-pick-now { margin: 4px 0 0; } +} + /* ---------- tabletas y pantallas anchas ---------- */ /* a 800dp todo se estiraba: lineas larguisimas y la hoja del menu con cinco iconos repartidos en 800px. Contenido a 720px como mucho, centrado; la hoja, igual */ diff --git a/src/client/assets/translations/i18n_fork.js b/src/client/assets/translations/i18n_fork.js index 567fcd6..8d0573a 100644 --- a/src/client/assets/translations/i18n_fork.js +++ b/src/client/assets/translations/i18n_fork.js @@ -20,6 +20,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menu", bbMenuClose: "Close", + uxPickHint: "Tap one and it is saved. You can change it any time in Settings.", + uxPickCurrent: "In use", workflow_acampada: "Camp", tribeSectionsMore: "More", tribeSectionsLabel: "Sections", @@ -100,6 +102,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menú", bbMenuClose: "Cerrar", + uxPickHint: "Toca uno y queda guardado. Puedes cambiarlo cuando quieras en Ajustes.", + uxPickCurrent: "En uso", workflow_acampada: "Acampada", tribeSectionsMore: "Más", tribeSectionsLabel: "Secciones", @@ -186,6 +190,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menu", bbMenuClose: "Fermer", + uxPickHint: "Touchez-en un et c'est enregistré. Vous pouvez le changer à tout moment dans les Paramètres.", + uxPickCurrent: "Actuel", workflow_acampada: "Campement", tribeSectionsMore: "Plus", tribeSectionsLabel: "Sections", @@ -266,6 +272,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menua", bbMenuClose: "Itxi", + uxPickHint: "Bat ukitu eta gordeta geratzen da. Nahi duzunean alda dezakezu Ezarpenetan.", + uxPickCurrent: "Erabiltzen", workflow_acampada: "Kanpaldia", tribeSectionsMore: "Gehiago", tribeSectionsLabel: "Atalak", @@ -346,6 +354,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menü", bbMenuClose: "Schließen", + uxPickHint: "Tippe eins an und es ist gespeichert. Du kannst es jederzeit in den Einstellungen ändern.", + uxPickCurrent: "Aktiv", workflow_acampada: "Camp", tribeSectionsMore: "Mehr", tribeSectionsLabel: "Bereiche", @@ -426,6 +436,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menu", bbMenuClose: "Chiudi", + uxPickHint: "Toccane uno ed è salvato. Puoi cambiarlo quando vuoi nelle Impostazioni.", + uxPickCurrent: "In uso", workflow_acampada: "Accampata", tribeSectionsMore: "Altro", tribeSectionsLabel: "Sezioni", @@ -506,6 +518,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Menu", bbMenuClose: "Fechar", + uxPickHint: "Toque num e fica guardado. Pode mudá-lo quando quiser nas Definições.", + uxPickCurrent: "Em uso", workflow_acampada: "Acampada", tribeSectionsMore: "Mais", tribeSectionsLabel: "Secções", @@ -586,6 +600,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "菜单", bbMenuClose: "关闭", + uxPickHint: "点一下即保存。随时可以在设置中更改。", + uxPickCurrent: "使用中", workflow_acampada: "营地", tribeSectionsMore: "更多", tribeSectionsLabel: "板块", @@ -666,6 +682,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "القائمة", bbMenuClose: "إغلاق", + uxPickHint: "المس واحدًا ويُحفظ. يمكنك تغييره في أي وقت من الإعدادات.", + uxPickCurrent: "قيد الاستخدام", workflow_acampada: "مخيم", tribeSectionsMore: "المزيد", tribeSectionsLabel: "الأقسام", @@ -746,6 +764,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "मेनू", bbMenuClose: "बंद करें", + uxPickHint: "एक पर टैप करें और वह सेव हो जाएगा। सेटिंग्स में इसे कभी भी बदल सकते हैं।", + uxPickCurrent: "चालू", workflow_acampada: "शिविर", tribeSectionsMore: "और", tribeSectionsLabel: "खंड", @@ -826,6 +846,8 @@ module.exports = { // usabilidad (19/10/2026): votaciones, borrar, modulos, pie bbMenu: "Меню", bbMenuClose: "Закрыть", + uxPickHint: "Нажмите на вариант, и он сохранится. Изменить можно в любой момент в Настройках.", + uxPickCurrent: "Сейчас", workflow_acampada: "Лагерь", tribeSectionsMore: "Ещё", tribeSectionsLabel: "Разделы", diff --git a/src/views/mobile_ui.js b/src/views/mobile_ui.js index 4b2df21..33dc310 100644 --- a/src/views/mobile_ui.js +++ b/src/views/mobile_ui.js @@ -10,7 +10,7 @@ // i18n se lee al llamar (main_views lo cambia en cada peticion y este fichero se carga // antes que el). -const { a, button, details, div, form, input, label, nav, p, progress, span, summary } = require("../server/node_modules/hyperaxe"); +const { a, button, details, div, form, img, input, label, nav, p, progress, span, summary } = require("../server/node_modules/hyperaxe"); const { iconNode } = require("./mobile_icons"); const isMobile = () => process.env.OASIS_MOBILE === '1'; @@ -109,7 +109,7 @@ const tribeTabs = (tribe, section, sections) => { // primero lo que se toca (idioma, aspecto, tipo de uso, portada) y despues lo tecnico; // se reordena por id de seccion, sin tocar las secciones -const SETTINGS_FIRST = ['update', 'language', 'theme', 'workflows', 'home-page']; +const SETTINGS_FIRST = ['update', 'language', 'ux', 'theme', 'workflows', 'home-page']; const settingsOrder = (parts) => { const list = parts.filter(Boolean); const rank = (el) => { const i = SETTINGS_FIRST.indexOf(el && el.id); return i === -1 ? SETTINGS_FIRST.length : i; }; @@ -128,6 +128,35 @@ const languagePick = () => { ); }; +// tipo de portada (ux): cada tarjeta es un boton que guarda al tocarlo. Antes eran +// radios y un boton de guardar debajo de seis tarjetas grandes, fuera de pantalla. +// Mismos modos y mismas condiciones que settings_view y welcome_view; las rutas +// (/settings/ux, /welcome/ux) son las del dev +const uxPick = (action, cfg, withHint) => { + const i18n = t(); + const mods = (cfg && cfg.modules) || {}; + const ux = cfg && cfg.ux && cfg.ux.current; + const modes = [ + ['blocks', i18n.uxModeMenus || 'Blocks', true], + ['ainav', i18n.uxModeAINav || 'AI', mods.aiNavMod === 'on'], + ['chats', i18n.uxModeChats || 'Conversations', mods.chatsMod === 'on'], + ['feed', i18n.uxModeFeed || 'Microblogging', true], + ['phone', i18n.phoneTitle || 'Phone', mods.phoneMod !== 'off'], + ['rooms', i18n.roomsTitle || 'Rooms', mods.roomsMod !== 'off'] + ].filter(m => m[2]); + const cur = modes.some(m => m[0] === ux) ? ux : 'blocks'; + return form({ method: 'POST', action, class: 'ux-pick' }, + withHint ? p({ class: 'ux-pick-hint' }, i18n.uxPickHint || 'Tap one and it is saved. You can change it any time in Settings.') : null, + div({ class: 'ux-pick-grid' }, + ...modes.map(([value, title]) => button(Object.assign({ type: 'submit', name: 'ux', value, class: value === cur ? 'ux-pick-card is-current' : 'ux-pick-card' }, value === cur ? { 'aria-current': 'true' } : {}), + img({ src: `/assets/images/ux-${value}.png`, class: 'ux-pick-shot', alt: '' }), + span({ class: 'ux-pick-label' }, title), + value === cur ? span({ class: 'ux-pick-now' }, i18n.uxPickCurrent || 'In use') : null + )) + ) + ); +}; + // ---------- acciones de tarjeta ---------- // borrar pide un segundo toque: la X abre "¿Borrar? Si, borrar" y solo ese boton @@ -171,6 +200,6 @@ const welcomeBannerAction = () => t().welcomeBannerActionMobile || t().welcomeBa module.exports = { isMobile, VOTE_KINDS, voteKindPicker, voteBars, voteOwnNote, tribeRow, tribeTabs, - settingsOrder, languagePick, deleteConfirm, footerLine, + settingsOrder, languagePick, uxPick, deleteConfirm, footerLine, welcomeBannerHere, welcomeBannerText, welcomeBannerAction }; diff --git a/src/views/settings_view.js b/src/views/settings_view.js index 1bc0f73..879766d 100644 --- a/src/views/settings_view.js +++ b/src/views/settings_view.js @@ -175,7 +175,7 @@ const settingsView = (params) => { div({ class: "tags-header" }, h2(i18n.uxModeTitle || "UX"), p(i18n.uxModeDescription || "Select which UX navigation mode you want for your GUI."), - form( + isMobile ? mobileUi.uxPick("/settings/ux", currentConfig) : form( { action: "/settings/ux", method: "POST" }, (() => { const aiNavEnabled = currentConfig.modules && currentConfig.modules.aiNavMod === 'on'; diff --git a/src/views/welcome_view.js b/src/views/welcome_view.js index c41f586..1d408e4 100644 --- a/src/views/welcome_view.js +++ b/src/views/welcome_view.js @@ -39,6 +39,7 @@ const stepAction = (key, profile, status) => { let cfg = {} try { cfg = require("../configs/config-manager.js").getConfig() || {} } catch (_) {} const cur = ["ainav", "chats", "feed", "phone", "rooms"].includes(cfg.ux?.current) ? cfg.ux.current : "blocks" + if (mobileUi.isMobile()) return mobileUi.uxPick("/welcome/ux", cfg, true) return form({ method: "POST", action: "/welcome/ux", class: "welcome-ux-form" }, div({ class: "welcome-ux-grid" }, pickCard("ux", "blocks", cur, i18n.uxModeMenus, "/assets/images/ux-blocks.png"),