WIP: mejoras de fidelidad, editor, lógica y suite e2e (checkpoint tras reinicio)
This commit is contained in:
+110
-37
@@ -12,6 +12,7 @@
|
||||
<link rel="stylesheet" href="css/iphone.css">
|
||||
<link rel="stylesheet" href="css/whatsapp.css">
|
||||
<link rel="stylesheet" href="css/editor.css">
|
||||
<link rel="stylesheet" href="css/interactividad.css">
|
||||
</head>
|
||||
<body data-tema="claro">
|
||||
|
||||
@@ -27,64 +28,119 @@
|
||||
<h1>WhatsApp Cloud API</h1>
|
||||
<span class="toolbar-subtitulo">Mockup visual · demo estática</span>
|
||||
</div>
|
||||
<!-- Estado del guardado local (lo pinta js/ux.js) -->
|
||||
<span id="estado-guardado" class="tb-guardado" data-estado="ok" title="Guardado en este navegador">
|
||||
<span class="tb-guardado-punto" aria-hidden="true"></span>
|
||||
<span class="tb-guardado-txt">Guardado</span>
|
||||
<span class="tb-guardado-rel" aria-hidden="true"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-acciones">
|
||||
<button id="btn-tema" class="tb-btn" type="button" title="Alternar tema claro / oscuro" aria-label="Alternar tema">
|
||||
<button id="btn-deshacer" class="tb-btn tb-btn-icono" type="button" title="Deshacer (Ctrl+Z)" aria-label="Deshacer" disabled>
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 14 4 9l5-5"/><path d="M4 9h10.5a5.5 5.5 0 0 1 0 11H11"/></svg>
|
||||
</button>
|
||||
<button id="btn-rehacer" class="tb-btn tb-btn-icono" type="button" title="Rehacer (Ctrl+Mayús+Z)" aria-label="Rehacer" disabled>
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 14 5-5-5-5"/><path d="M20 9H9.5a5.5 5.5 0 0 0 0 11H13"/></svg>
|
||||
</button>
|
||||
<span class="tb-separador" aria-hidden="true"></span>
|
||||
<button id="btn-tema" class="tb-btn" type="button" title="Alternar tema claro / oscuro (T)" aria-label="Tema oscuro" aria-pressed="false">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 2.6a9.4 9.4 0 1 0 0 18.8 9.4 9.4 0 0 0 0-18.8zm0 16.9V4.5a7.5 7.5 0 0 1 0 15z"/></svg>
|
||||
<span>Tema</span>
|
||||
</button>
|
||||
<button id="btn-presentacion" class="tb-btn" type="button" title="Modo presentación (oculta el editor)">
|
||||
<button id="btn-presentacion" class="tb-btn" type="button" title="Modo presentación: oculta el editor (P · Esc para salir)" aria-pressed="false">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" fill-rule="evenodd" aria-hidden="true"><path d="M3 4.5h18a1 1 0 0 1 1 1V16a1 1 0 0 1-1 1h-6.4v2.1h1.9a.9.9 0 1 1 0 1.8H7.5a.9.9 0 1 1 0-1.8h1.9V17H3a1 1 0 0 1-1-1V5.5a1 1 0 0 1 1-1zm8 3.4v5.7l4.6-2.85z"/></svg>
|
||||
<span>Presentación</span>
|
||||
</button>
|
||||
<span class="tb-separador" aria-hidden="true"></span>
|
||||
<button id="btn-exportar" class="tb-btn" type="button" title="Exportar el estado a JSON">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 3.2c.5 0 .9.4.9.9v8.2l2.7-2.7a.9.9 0 1 1 1.3 1.3l-4.2 4.2a.9.9 0 0 1-1.3 0L7.2 11a.9.9 0 1 1 1.3-1.3l2.7 2.7V4.1c0-.5.4-.9.9-.9zM4.9 18.5h14.2a.9.9 0 1 1 0 1.8H4.9a.9.9 0 1 1 0-1.8z"/></svg>
|
||||
<span>Exportar</span>
|
||||
</button>
|
||||
<button id="btn-importar" class="tb-btn" type="button" title="Importar un estado JSON">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 15.6c-.5 0-.9-.4-.9-.9V6.5L8.4 9.2A.9.9 0 1 1 7.1 7.9l4.2-4.2a.9.9 0 0 1 1.3 0l4.2 4.2a.9.9 0 1 1-1.3 1.3l-2.7-2.7v8.2c0 .5-.4.9-.9.9zM4.9 18.5h14.2a.9.9 0 1 1 0 1.8H4.9a.9.9 0 1 1 0-1.8z"/></svg>
|
||||
<span>Importar</span>
|
||||
</button>
|
||||
<input type="file" id="importar-archivo" accept="application/json,.json" hidden>
|
||||
<button id="btn-reset" class="tb-btn" type="button" title="Restablecer la demo (borra localStorage)">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 5V2.8a.7.7 0 0 0-1.13-.55L6.9 5.2a.7.7 0 0 0 0 1.1l3.97 2.95A.7.7 0 0 0 12 8.7V6.5a5.5 5.5 0 1 1-5.5 5.5H4.75A7.25 7.25 0 1 0 12 5z"/></svg>
|
||||
<span>Restablecer</span>
|
||||
<span class="tb-separador tb-sep-sec" aria-hidden="true"></span>
|
||||
<!-- «⋯»: en ventana estrecha agrupa las acciones secundarias -->
|
||||
<button id="btn-mas" class="tb-btn tb-btn-icono" type="button" aria-label="Más acciones" aria-expanded="false" aria-controls="tb-menu" title="Más acciones">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><circle cx="5" cy="12" r="1.9"/><circle cx="12" cy="12" r="1.9"/><circle cx="19" cy="12" r="1.9"/></svg>
|
||||
</button>
|
||||
<div id="tb-menu" class="tb-grupo-sec">
|
||||
<button id="btn-demos" class="tb-btn" type="button" title="Nueva demo: escenarios por industria y demos guardadas (N)">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3.5" y="3.5" width="7" height="7" rx="1.6"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.6"/><rect x="3.5" y="13.5" width="7" height="7" rx="1.6"/><path d="M17 14v6M14 17h6"/></svg>
|
||||
<span>Demos</span>
|
||||
</button>
|
||||
<button id="btn-exportar" class="tb-btn" type="button" title="Exportar la demo a JSON (Ctrl+S)">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 3.2c.5 0 .9.4.9.9v8.2l2.7-2.7a.9.9 0 1 1 1.3 1.3l-4.2 4.2a.9.9 0 0 1-1.3 0L7.2 11a.9.9 0 1 1 1.3-1.3l2.7 2.7V4.1c0-.5.4-.9.9-.9zM4.9 18.5h14.2a.9.9 0 1 1 0 1.8H4.9a.9.9 0 1 1 0-1.8z"/></svg>
|
||||
<span>Exportar</span>
|
||||
</button>
|
||||
<button id="btn-importar" class="tb-btn" type="button" title="Importar una demo desde JSON (sustituye la actual)">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 15.6c-.5 0-.9-.4-.9-.9V6.5L8.4 9.2A.9.9 0 1 1 7.1 7.9l4.2-4.2a.9.9 0 0 1 1.3 0l4.2 4.2a.9.9 0 1 1-1.3 1.3l-2.7-2.7v8.2c0 .5-.4.9-.9.9zM4.9 18.5h14.2a.9.9 0 1 1 0 1.8H4.9a.9.9 0 1 1 0-1.8z"/></svg>
|
||||
<span>Importar</span>
|
||||
</button>
|
||||
<input type="file" id="importar-archivo" accept="application/json,.json" hidden>
|
||||
<button id="btn-reset" class="tb-btn" type="button" title="Restablecer la demo de ejemplo (pide confirmación)">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="currentColor" aria-hidden="true"><path d="M12 5V2.8a.7.7 0 0 0-1.13-.55L6.9 5.2a.7.7 0 0 0 0 1.1l3.97 2.95A.7.7 0 0 0 12 8.7V6.5a5.5 5.5 0 1 1-5.5 5.5H4.75A7.25 7.25 0 1 0 12 5z"/></svg>
|
||||
<span>Restablecer</span>
|
||||
</button>
|
||||
<button id="btn-atajos" class="tb-btn tb-btn-icono" type="button" title="Atajos de teclado (?)" aria-label="Atajos de teclado">
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="6" width="19" height="12" rx="2.2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M7.5 14h9"/></svg>
|
||||
<span class="tb-solo-menu">Atajos</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- ============ Panel editor (columna izquierda · contenido: módulo C) ============ -->
|
||||
<aside id="panel-editor" aria-label="Editor del mockup">
|
||||
<section class="wam-bloque">
|
||||
<h2 class="wam-bloque-titulo">Cuenta</h2>
|
||||
<!-- Cuenta: se edita una vez por demo; plegada si ya hay mensajes -->
|
||||
<details id="bloque-cuenta" class="wam-bloque ed-bloque-cuenta" open>
|
||||
<summary class="ed-cuenta-sumario">
|
||||
<span class="wam-bloque-titulo">Cuenta</span>
|
||||
<span id="cuenta-resumen" class="ed-cuenta-resumen"></span>
|
||||
</summary>
|
||||
<!-- El formulario de identidad lo pinta js/editor.js (C) en runtime -->
|
||||
<div id="editor-cuenta"></div>
|
||||
</section>
|
||||
</details>
|
||||
|
||||
<section class="wam-bloque">
|
||||
<h2 class="wam-bloque-titulo">Mensajes</h2>
|
||||
<section class="wam-bloque ed-bloque-mensajes" aria-labelledby="titulo-mensajes">
|
||||
<div class="ed-bloque-cab">
|
||||
<h2 id="titulo-mensajes" class="wam-bloque-titulo">Mensajes</h2>
|
||||
<button id="ed-avisos-global" class="ed-avisos-global" type="button" data-acc="ir-aviso" hidden></button>
|
||||
</div>
|
||||
<div class="wam-alta">
|
||||
<!-- Paleta de tarjetas de los 13 tipos; la rellena js/editor.js (C) en runtime.
|
||||
El tipo elegido queda en #nuevo-tipo-valor (hidden) y en data-valor de la paleta. -->
|
||||
<div id="nuevo-tipo" class="ed-paleta" role="radiogroup" aria-label="Tipo de mensaje a añadir"></div>
|
||||
<!-- Paleta de los 13 tipos (la rellena js/editor.js): un clic AÑADE el mensaje.
|
||||
El último tipo usado queda en #nuevo-tipo-valor (hidden) y en data-valor. -->
|
||||
<div id="nuevo-tipo" class="ed-paleta" role="toolbar" aria-label="Añadir un mensaje: elige el tipo"></div>
|
||||
<input type="hidden" id="nuevo-tipo-valor" value="text">
|
||||
<button id="btn-anadir" class="wam-btn-anadir" type="button">Añadir</button>
|
||||
<div class="ed-alta-pie">
|
||||
<span id="ed-destino-alta" class="ed-destino-alta" aria-live="polite"></span>
|
||||
<button id="btn-anadir" class="wam-btn-anadir" type="button">+ Otro «Texto»</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ed-lista-herr">
|
||||
<button type="button" class="ed-btn ed-btn-mini" data-acc="horas-secuencia" title="Reparte las horas desde la del primer mensaje, +1 min por mensaje">Horas en secuencia</button>
|
||||
<button type="button" class="ed-btn ed-btn-mini" data-acc="plegar-todo">Plegar todo</button>
|
||||
</div>
|
||||
<!-- Una tarjeta colapsable por mensaje la pinta js/editor.js (C) -->
|
||||
<div id="lista-mensajes"></div>
|
||||
</section>
|
||||
|
||||
<!-- Solo en ventana estrecha: salta a la vista previa del mensaje en edición -->
|
||||
<button id="btn-ver-telefono" class="ed-ver-telefono" type="button">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="6.5" y="2.5" width="11" height="19" rx="2.5"/><path d="M11 18.5h2"/></svg>
|
||||
Ver teléfono
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<!-- ============ Panel móvil (columna derecha · shell A) ============ -->
|
||||
<section id="panel-movil" aria-label="Vista previa en un iPhone">
|
||||
<div id="barra-movil">
|
||||
<span class="barra-titulo">Vista previa</span>
|
||||
<button id="btn-reproducir" type="button" title="Reproducir la demo paso a paso (clic de nuevo para detener)">
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M8.4 5.6v12.8c0 .8.9 1.3 1.6.9l10-6.4c.6-.4.6-1.4 0-1.8l-10-6.4c-.7-.4-1.6.1-1.6.9z"/></svg>
|
||||
<span>Reproducir demo</span>
|
||||
</button>
|
||||
<div class="barra-acciones">
|
||||
<select id="sel-velocidad" title="Velocidad de la reproducción" aria-label="Velocidad de la reproducción">
|
||||
<option value="0.5">0,5x</option>
|
||||
<option value="1" selected>1x</option>
|
||||
<option value="2">2x</option>
|
||||
</select>
|
||||
<button id="btn-reiniciar" type="button" title="Descartar lo pulsado y escrito en el teléfono y volver al guion">Reiniciar chat</button>
|
||||
<button id="btn-reproducir" type="button" title="Reproducir la demo paso a paso (clic de nuevo para detener)">
|
||||
<svg viewBox="0 0 24 24" width="15" height="15" fill="currentColor" aria-hidden="true"><path d="M8.4 5.6v12.8c0 .8.9 1.3 1.6.9l10-6.4c.6-.4.6-1.4 0-1.8l-10-6.4c-.7-.4-1.6.1-1.6.9z"/></svg>
|
||||
<span>Reproducir demo</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="iphone-escena">
|
||||
@@ -150,18 +206,21 @@
|
||||
|
||||
<!-- Composer (estructura A · lógica D) -->
|
||||
<form id="composer" class="compose" autocomplete="off">
|
||||
<button type="button" class="attach" aria-label="Adjuntar">
|
||||
<svg viewBox="0 0 24 24" width="25" height="25" fill="currentColor" aria-hidden="true"><path d="M11.1 11.1V5.4a.9.9 0 1 1 1.8 0v5.7h5.7a.9.9 0 1 1 0 1.8h-5.7v5.7a.9.9 0 1 1-1.8 0v-5.7H5.4a.9.9 0 1 1 0-1.8z"/></svg>
|
||||
<button type="button" class="attach" aria-label="Adjuntar" tabindex="-1">
|
||||
<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" aria-hidden="true"><path d="M12 4v16M4 12h16"/></svg>
|
||||
</button>
|
||||
<div class="field">
|
||||
<input id="composer-input" type="text" placeholder="Mensaje" aria-label="Mensaje" autocomplete="off" enterkeyhint="send">
|
||||
<svg class="mic" viewBox="0 0 24 24" width="21" height="21" fill="currentColor" aria-hidden="true">
|
||||
<path d="M12 13.6a3.2 3.2 0 0 0 3.2-3.2V5.2a3.2 3.2 0 1 0-6.4 0v5.2A3.2 3.2 0 0 0 12 13.6z"/>
|
||||
<path d="M18.4 10.4a.9.9 0 1 0-1.8 0 4.6 4.6 0 0 1-9.2 0 .9.9 0 1 0-1.8 0 6.4 6.4 0 0 0 5.5 6.3v2.1H8.6a.9.9 0 1 0 0 1.8h6.8a.9.9 0 1 0 0-1.8h-2.5v-2.1a6.4 6.4 0 0 0 5.5-6.3z"/>
|
||||
</svg>
|
||||
<input id="composer-input" type="text" placeholder=" " aria-label="Mensaje" autocomplete="off" enterkeyhint="send">
|
||||
<svg class="sticker" viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M20 12.8V8a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v8a4 4 0 0 0 4 4h4.8Z"/><path d="M20 12.8h-3.4a3.8 3.8 0 0 0-3.8 3.8V20"/></svg>
|
||||
</div>
|
||||
<button type="button" class="cmp-icono cmp-camara" aria-label="Cámara" tabindex="-1">
|
||||
<svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" aria-hidden="true"><path d="M3 8.6A2.1 2.1 0 0 1 5.1 6.5h2.3l1.5-2h6.2l1.5 2h2.3A2.1 2.1 0 0 1 21 8.6v8.8a2.1 2.1 0 0 1-2.1 2.1H5.1A2.1 2.1 0 0 1 3 17.4Z"/><circle cx="12" cy="12.8" r="3.7"/></svg>
|
||||
</button>
|
||||
<button type="button" class="cmp-icono cmp-mic" aria-label="Nota de voz" tabindex="-1">
|
||||
<svg viewBox="0 0 24 24" width="24" height="26" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><rect x="8.6" y="2.8" width="6.8" height="11.6" rx="3.4"/><path d="M5.6 11a6.4 6.4 0 0 0 12.8 0M12 17.4v3.8"/></svg>
|
||||
</button>
|
||||
<button id="composer-enviar" class="enviar" type="submit" aria-label="Enviar mensaje">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M3.5 20.2 21.4 12 3.5 3.8v6.3L15 12 3.5 13.9z"/></svg>
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor" aria-hidden="true"><path d="M4.4 19.6 21 12 4.4 4.4l.1 5.9L15 12 4.5 13.7Z"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -180,6 +239,18 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Ventana estrecha (≤ 900 px): pestañas fijas Editor / Vista previa (js/ux.js) -->
|
||||
<nav id="tabs-movil" class="tabs-movil" role="tablist" aria-label="Vista">
|
||||
<button id="tab-editor" class="tab-movil" type="button" role="tab" aria-selected="false" aria-controls="panel-editor" tabindex="-1">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="m13.5 6.5 4 4"/></svg>
|
||||
<span>Editor</span>
|
||||
</button>
|
||||
<button id="tab-vista" class="tab-movil" type="button" role="tab" aria-selected="true" aria-controls="panel-movil">
|
||||
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" aria-hidden="true"><rect x="6.5" y="2.5" width="11" height="19" rx="2.5"/><path d="M11 18.5h2"/></svg>
|
||||
<span>Vista previa</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
<noscript><p class="wam-noscript">Esta demo necesita JavaScript habilitado.</p></noscript>
|
||||
@@ -190,7 +261,9 @@
|
||||
<script src="js/render.js"></script> <!-- B: render del chat; NO toca DOM al cargar -->
|
||||
<script src="js/editor.js"></script> <!-- C: formularios del editor; NO toca DOM al cargar -->
|
||||
<script src="js/interactivity.js"></script> <!-- D: flujos/typing/demo; NO toca DOM al cargar -->
|
||||
<script src="js/main.js"></script> <!-- D: única ejecución — WAM.init() -->
|
||||
<script src="js/escenarios.js"></script> <!-- C: escenarios por industria (datos); NO toca DOM al cargar -->
|
||||
<script src="js/ux.js"></script> <!-- C: deshacer, diálogos, atajos, pestañas; WAM.initUX() lo llama renderEditor -->
|
||||
<script src="js/main.js"></script> <!-- D: única ejecución — WAM.init() -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user