diff --git a/src/backend/backend.js b/src/backend/backend.js index 6c69274..f11d2d7 100644 --- a/src/backend/backend.js +++ b/src/backend/backend.js @@ -1806,7 +1806,7 @@ router ctx.body = statsView(stats, filter); }) .get("/modules", async (ctx) => { - const modules = ['blogs', 'polls', 'fediverse', 'invites', 'wallet', 'legacy', 'dev', 'cipher', 'bookmarks', 'calendars', 'chats', 'videos', 'docs', 'audios', 'tags', 'images', 'maps', 'trending', 'events', 'tasks', 'market', 'tribes', 'larp', 'votes', 'reports', 'opinions', 'pads', 'transfers', 'feed', 'pixelia', 'melody', 'agenda', 'favorites', 'ai', 'forum', 'games', 'housing', 'jobs', 'projects', 'industry', 'shops', 'banking', 'parliament', 'courts']; + const modules = ['blogs', 'polls', 'karvan', 'fediverse', 'invites', 'wallet', 'legacy', 'dev', 'cipher', 'bookmarks', 'calendars', 'chats', 'videos', 'docs', 'audios', 'tags', 'images', 'maps', 'trending', 'events', 'tasks', 'market', 'tribes', 'larp', 'votes', 'reports', 'opinions', 'pads', 'transfers', 'feed', 'pixelia', 'melody', 'agenda', 'favorites', 'ai', 'forum', 'games', 'housing', 'jobs', 'projects', 'industry', 'shops', 'banking', 'parliament', 'courts']; const cfg = getConfig().modules; ctx.body = modulesView(modules.reduce((acc, m) => { acc[`${m}Mod`] = cfg[`${m}Mod`]; return acc; }, {})); }) diff --git a/src/client/assets/styles/karvan.css b/src/client/assets/styles/karvan.css new file mode 100644 index 0000000..7620c14 --- /dev/null +++ b/src/client/assets/styles/karvan.css @@ -0,0 +1,88 @@ +/* Estilos del modulo Karvan (salas efimeras y llamadas). + * + * Viven aparte del tema para que el modulo se vea igual en cualquier tema y + * tambien en escritorio, donde OasisMobile.css no se carga. Mismo patron que + * highlight.css, que tampoco pertenece a ningun tema concreto. + */ + +/* --- Reply a mensaje en chats (server-side ?replyTo=, sin JS) --- */ +.chat-message-meta .chat-reply-btn { margin-left: auto; padding: 0 4px; color: #FFD700; text-decoration: none; font-size: 15px; opacity: .85; } +.chat-message-self .chat-message-meta .chat-reply-btn { margin-left: 8px; } +.chat-quote { + display: block; border-left: 3px solid #FFD700; padding: 3px 8px; margin: 0 0 5px; + background: rgba(255,215,0,.08); border-radius: 6px; font-size: 12px; line-height: 1.3; +} +.chat-quote-author, .chat-quote-author a { color: #FFD700; font-weight: 700; margin-right: 6px; } +.chat-quote-text { color: #FFDD44; } +.chat-replying-to { + display: flex; flex-wrap: wrap; align-items: center; gap: 6px; + border-left: 3px solid #FFD700; background: rgba(255,215,0,.10); + padding: 6px 10px; margin-bottom: 8px; border-radius: 8px; font-size: 12px; +} +.chat-replying-label { color: #FFD700; font-weight: 700; } +.chat-reply-cancel { margin-left: auto; color: #FFD700; text-decoration: none; font-weight: 700; font-size: 14px; } +/* ========================================================================== + FORK_IA_UX · Módulo KARVAN (mensajes temporales + WebRTC) — chat estilo burbujas + ========================================================================== */ +.karvan-intro h2 { color: #FFD700; margin: 0 0 6px; } +.karvan-sub { color: #FFDD44; font-size: 13px; } +.karvan-create input[type="text"] { width: 100%; box-sizing: border-box; padding: 10px 12px; + background: #161616; border: 1px solid #333; border-radius: 10px; color: #FFD700; } +.karvan-ttl { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 10px 0; } +.karvan-ttl-lbl { color: #FFDD44; font-size: 13px; } +.karvan-ttl-opt { display: inline-flex; align-items: center; gap: 4px; color: #FFD700; font-size: 13px; } +.karvan-create button, .karvan-compose button { padding: 10px 16px; border: 1px solid #FFD700; + border-radius: 10px; background: #FFD700; color: #121212; font-weight: 700; cursor: pointer; } +.karvan-rooms { list-style: none; margin: 8px 0 0; padding: 0; } +.karvan-rooms li { margin: 0 0 8px; } +.karvan-room-item { display: flex; justify-content: space-between; align-items: center; gap: 10px; + padding: 12px 14px; background: #161616; border: 1px solid #333; border-radius: 12px; + color: #FFD700; text-decoration: none; min-height: 48px; box-sizing: border-box; } +.karvan-room-item .kr-title { font-weight: 600; } +.karvan-room-item .kr-meta { color: #FFDD44; font-size: 12px; white-space: nowrap; } +.karvan-empty { color: #FFDD44; } +/* sala */ +.karvan-room-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; } +.karvan-back { color: #FFD700; text-decoration: none; font-weight: 600; } +.karvan-room-title { flex: 1 1 auto; color: #FFD700; font-weight: 700; text-align: center; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.karvan-expire { color: #FFDD44; font-size: 12px; white-space: nowrap; } +.karvan-live { display: inline-flex; align-items: center; gap: 6px; color: #FFDD44; font-size: 12px; margin-bottom: 8px; } +.karvan-live .kl-dot { width: 8px; height: 8px; border-radius: 50%; background: #777; } +.karvan-live.on .kl-dot { background: #35d07f; } +.karvan-live.on .kl-txt { color: #35d07f; } +.karvan-msgs { list-style: none; margin: 0; padding: 6px 0; display: flex; flex-direction: column; gap: 6px; + max-height: 58vh; overflow-y: auto; } +.karvan-msg { max-width: 82%; align-self: flex-start; background: #1c1c1c; border: 1px solid #333; + border-radius: 14px; padding: 7px 11px; box-sizing: border-box; } +.karvan-msg-self { align-self: flex-end; background: #2a2412; border-color: #FFD700; } +.karvan-msg-live { border-left: 2px solid #35d07f; } +.karvan-msg .km-from { display: block; font-size: 10px; color: #FFDD44; margin-bottom: 2px; } +.karvan-msg .km-text { color: #FFD700; word-break: break-word; } +.karvan-compose { display: flex; gap: 8px; margin-top: 10px; } +.karvan-compose input[type="text"] { flex: 1 1 auto; min-width: 0; padding: 10px 12px; + background: #161616; border: 1px solid #333; border-radius: 10px; color: #FFD700; } +/* Fase 2: invitar a un contacto por feed id */ +.karvan-invite { display: flex; gap: 8px; margin-top: 8px; } +.karvan-invite input[type="text"] { flex: 1 1 auto; min-width: 0; padding: 9px 12px; + background: #161616; border: 1px solid #333; border-radius: 10px; color: #FFD700; font-family: monospace; font-size: 12px; } +.karvan-invite button { flex: 0 0 auto; padding: 9px 14px; border: 1px solid #FFD700; background: #161616; color: #FFD700; border-radius: 10px; cursor: pointer; } +.karvan-invite-msg { font-size: 12px; margin: 6px 0 0; } +.karvan-invite-msg.ok { color: #35d07f; } +.karvan-invite-msg.err { color: #ff8a8a; } +/* --- Karvan videollamada (Fase 1): media P2P sobre el mismo WebRTC del chat --- */ +.karvan-call, .karvan-videos, .karvan-call-bar, .kc-status { + background-color: transparent; box-shadow: none; border-radius: 0; padding: 0; margin: 0; /* anular div{#222} +.karvan-call { margin: 10px 0; } +.karvan-videos { display: none; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 6px; margin-bottom: 8px; } +.karvan-call.active .karvan-videos { display: grid; } +.kc-video { width: 100%; aspect-ratio: 4 / 3; background: #000; border-radius: 10px; border: 1px solid #333; object-fit: cover; display: block; } +.kc-local { transform: scaleX(-1); } +.kc-remote { display: contents; } +.karvan-call-bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } +.kc-btn { min-height: 44px; min-width: 44px; padding: 8px 12px; border-radius: 999px; + border: 1px solid #333; background: #161616; color: #FFD700; line-height: 1; cursor: pointer; } +.kc-btn.on { background: #FFD700; color: #121212; border-color: #FFD700; } +.kc-btn-start { font-weight: 600; } +.kc-btn-stop { background: #241414; border-color: #5a2a2a; } +.kc-status { font-size: 12px; color: #FFDD44; margin-top: 6px; min-height: 14px; } diff --git a/src/client/assets/translations/fork/i18n_fork.js b/src/client/assets/translations/fork/i18n_fork.js index cda9b98..ad4a727 100644 --- a/src/client/assets/translations/fork/i18n_fork.js +++ b/src/client/assets/translations/fork/i18n_fork.js @@ -11,6 +11,8 @@ module.exports = { en: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -55,6 +57,8 @@ module.exports = { spreadMore: "more" }, es: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Salas efimeras: los mensajes viven solo en memoria y se autodestruyen.", chatReply: "Responder", bbAdd: "Añadir", bbDone: "Hecho", @@ -104,6 +108,8 @@ module.exports = { totalspreads: "Replicas totales" }, fr: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -148,6 +154,8 @@ module.exports = { spreadMore: "plus" }, eu: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -192,6 +200,8 @@ module.exports = { spreadMore: "gehiago" }, de: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -236,6 +246,8 @@ module.exports = { spreadMore: "mehr" }, it: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -280,6 +292,8 @@ module.exports = { spreadMore: "altro" }, pt: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -324,6 +338,8 @@ module.exports = { spreadMore: "mais" }, zh: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -368,6 +384,8 @@ module.exports = { spreadMore: "更多" }, ar: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -412,6 +430,8 @@ module.exports = { spreadMore: "المزيد" }, hi: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", @@ -456,6 +476,8 @@ module.exports = { spreadMore: "और" }, ru: { + modulesKarvanLabel: "Karvan", + modulesKarvanDescription: "Ephemeral rooms: messages live only in memory and self-destruct.", chatReply: "Reply", bbAdd: "Add", bbDone: "Done", diff --git a/src/views/main_views.js b/src/views/main_views.js index f9058be..215a63d 100644 --- a/src/views/main_views.js +++ b/src/views/main_views.js @@ -537,6 +537,18 @@ const navGroup = ({ id, emoji, title, defaultOpen = false }, ...items) => { +const renderKarvanLink = () => { + const karvanMod = getConfig().modules.karvanMod === "on"; + return karvanMod + ? navLink({ + href: "/karvan", + emoji: "⧗", + text: i18n.karvanTitle, + class: "karvan-link enabled" + }) + : ""; +}; + const renderPollsLink = () => { const pollsMod = getConfig().modules.pollsMod === "on"; return pollsMod @@ -1202,6 +1214,9 @@ const template = (titlePrefix, ...elements) => { head( title(titlePrefix, " | Oasis"), link({ rel: "stylesheet", href: `/assets/styles/style.css?v=${assetVersion()}` }), + // el modulo Karvan trae su propia hoja: asi se ve igual en cualquier tema + // y tambien en escritorio, donde no se carga OasisMobile.css + link({ rel: "stylesheet", href: `/assets/styles/karvan.css?v=${assetVersion()}` }), // movil: el tema va DESPUES de mobile.css para ganar por cascada sin !important; // escritorio: orden original de epsylon ...(process.env.OASIS_MOBILE === '1' @@ -1507,7 +1522,8 @@ const template = (titlePrefix, ...elements) => { renderPadsLink(), renderForumLink(), renderMapsLink(), - renderChatsLink() + renderChatsLink(), + renderKarvanLink() ), navGroup( { @@ -1563,6 +1579,9 @@ exports.ainavHomeView = ({ recentTags = [] } = {}) => { head( title(placeholder, " | Oasis"), link({ rel: "stylesheet", href: `/assets/styles/style.css?v=${assetVersion()}` }), + // el modulo Karvan trae su propia hoja: asi se ve igual en cualquier tema + // y tambien en escritorio, donde no se carga OasisMobile.css + link({ rel: "stylesheet", href: `/assets/styles/karvan.css?v=${assetVersion()}` }), // movil: el tema va DESPUES de mobile.css para ganar por cascada sin !important; // escritorio: orden original de epsylon ...(process.env.OASIS_MOBILE === '1' diff --git a/src/views/modules_view.js b/src/views/modules_view.js index 606490d..857bc91 100644 --- a/src/views/modules_view.js +++ b/src/views/modules_view.js @@ -41,6 +41,7 @@ const modulesView = () => { { name: 'parliament', label: i18n.modulesParliamentLabel, description: i18n.modulesParliamentDescription }, { name: 'pixelia', label: i18n.modulesPixeliaLabel, description: i18n.modulesPixeliaDescription }, { name: 'polls', label: i18n.modulesPollsLabel, description: i18n.modulesPollsDescription }, + { name: 'karvan', label: i18n.modulesKarvanLabel, description: i18n.modulesKarvanDescription }, { name: 'projects', label: i18n.modulesProjectsLabel, description: i18n.modulesProjectsDescription }, { name: 'reports', label: i18n.modulesReportsLabel, description: i18n.modulesReportsDescription }, { name: 'shops', label: i18n.modulesShopsLabel, description: i18n.modulesShopsDescription },