Compila tu app web (Dialogflow)

Una app web es la IU de una acción que usa Interactive Canvas. Puedes usar tecnologías web existentes (HTML, CSS y JavaScript) para diseñar y desarrollar tu aplicación web. En su mayor parte, Interactive Canvas puede procesar contenido web como un navegador, pero se aplican algunas restricciones para la privacidad y seguridad del usuario. Antes de comenzar a diseñar tu IU, ten en cuenta los principios de diseño descritos en la sección Design guidelines.

El código HTML y JavaScript de la app web hace lo siguiente:

  • Registra callbacks de eventos de Interactive Canvas.
  • Inicializa la biblioteca JavaScript de Interactive Canvas.
  • Proporciona una lógica personalizada para actualizar tu app web según el estado.

En esta página, se explican las formas recomendadas de compilar tu app web, cómo habilitar la comunicación entre la app web y la entrega, y los lineamientos y las restricciones generales.

Si bien puedes usar cualquier método para compilar tu IU, Google recomienda que uses las siguientes bibliotecas:

Arquitectura

Google recomienda usar una arquitectura de aplicación de una sola página. Este enfoque permite un rendimiento óptimo y admite una experiencia conversacional continua del usuario. Interactive Canvas se puede usar en conjunto con frameworks de frontend, como Vue, Angular y React, que ayudan con la administración del estado.

Archivo HTML

El archivo HTML define el aspecto de tu IU. Este archivo también carga la biblioteca JavaScript de Interactive Canvas, que permite la comunicación entre tu aplicación web y tu acción conversacional.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Immersive Canvas Sample</title>
    <!-- Disable favicon requests -->
    <link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">
    <!-- Load Interactive Canvas JavaScript -->
    <script src="https://www.gstatic.com/assistant/df-asdk/interactivecanvas/api/interactive_canvas.min.js"></script>
    <!-- Load PixiJS for graphics rendering -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.8.7/pixi.min.js"></script>
    <!-- Load Stats.js for fps monitoring -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
    <!-- Load custom CSS -->
    <link rel="stylesheet" href="css/main.css">
  </head>
  <body>
    <div id="view" class="view">
      <div class="debug">
        <div class="stats"></div>
        <div class="logs"></div>
      </div>
    </div>
    <!-- Load custom JavaScript after elements are on page -->
    <script src="js/main.js"></script>
    <script src="js/log.js"></script>
  </body>
</html>

Comunicación entre la entrega y la app web

Ahora que compilaste tu app web y la entrega y las cargaste en la biblioteca interactiva de Canvas en el archivo de tu app web, debes definir cómo interactúan la app web y la entrega. Para ello, modifica los archivos que contienen la lógica de tu aplicación web.

action.js

Este archivo contiene el código para definir callbacks y, luego, invocar métodos mediante interactiveCanvas. Las devoluciones de llamada permiten que tu app web responda a información o solicitudes desde la acción conversacional, mientras que los métodos proporcionan una forma de enviar información o solicitudes a la acción conversacional.

Agrega interactiveCanvas.ready(callbacks); a tu archivo HTML para inicializar y registrar callbacks:

//action.js
class Action {
  constructor(scene) {
    this.canvas = window.interactiveCanvas;
    this.scene = scene;
    const that = this;
    this.commands = {
      TINT: function(data) {
        that.scene.sprite.tint = data.tint;
      },
      SPIN: function(data) {
        that.scene.sprite.spin = data.spin;
      },
      RESTART_GAME: function(data) {
        that.scene.button.texture = that.scene.button.textureButton;
        that.scene.sprite.spin = true;
        that.scene.sprite.tint = 0x0000FF; // blue
        that.scene.sprite.rotation = 0;
      },
    };
  }

  /**
   * Register all callbacks used by Interactive Canvas
   * executed during scene creation time.
   *
   */
  setCallbacks() {
    const that = this;
    // declare interactive canvas callbacks
    const callbacks = {
      onUpdate(data) {
        try {
          that.commands[data.command.toUpperCase()](data);
        } catch (e) {
          // do nothing, when no command is sent or found
        }
      },
    };
    // called by the Interactive Canvas web app once web app has loaded to
    // register callbacks
    this.canvas.ready(callbacks);
  }
}

main.js

Este archivo construye la escena de tu aplicación web. En este ejemplo, también controla los casos de éxito y error de la promesa que se mostró con sendTextQuery(). El siguiente es un extracto de main.js:

// main.js
const view = document.getElementById('view');
// initialize rendering and set correct sizing
this.renderer = PIXI.autoDetectRenderer({
  transparent: true,
  antialias: true,
  resolution: this.radio,
  width: view.clientWidth,
  height: view.clientHeight,
});
view.appendChild(this.element);

// center stage and normalize scaling for all resolutions
this.stage = new PIXI.Container();
this.stage.position.set(view.clientWidth / 2, view.clientHeight / 2);
this.stage.scale.set(Math.max(this.renderer.width,
    this.renderer.height) / 1024);

// load a sprite from a svg file
this.sprite = PIXI.Sprite.from('triangle.svg');
this.sprite.anchor.set(0.5);
this.sprite.tint = 0x00FF00; // green
this.sprite.spin = true;
this.stage.addChild(this.sprite);

// toggle spin on touch events of the triangle
this.sprite.interactive = true;
this.sprite.buttonMode = true;
this.sprite.on('pointerdown', () => {
  this.sprite.spin = !this.sprite.spin;
});

Compatibilidad con interacciones táctiles

Tu acción de Interactive Canvas puede responder tanto al tacto del usuario como a las entradas de voz. Según los lineamientos de diseño de Interactive Canvas, debes desarrollar tu acción para que "priorice la voz". Dicho esto, algunas pantallas inteligentes admiten interacciones táctiles.

La compatibilidad táctil es similar a la compatibilidad con respuestas conversacionales; sin embargo, en lugar de una respuesta vocal del usuario, el código JavaScript del cliente busca interacciones táctiles y las usa para cambiar elementos en la aplicación web.

Puedes ver un ejemplo de esto en la muestra, que utiliza la biblioteca Pixi.js:

...
this.sprite = PIXI.Sprite.from('triangle.svg');
...
this.sprite.interactive = true; // Enables interaction events
this.sprite.buttonMode = true; // Changes `cursor` property to `pointer` for PointerEvent
this.sprite.on('pointerdown', () => {
  this.sprite.spin = !this.sprite.spin;
});
...

En este caso, el valor de la variable spin se envía a través de la API de interactiveCanvas como una devolución de llamada update. La entrega tiene una lógica que activa un intent basado en el valor de spin.

...
app.intent('pause', (conv) => {
  conv.ask(`Ok, I paused spinning. What else?`);
  conv.ask(new HtmlResponse({
    data: {
      spin: false,
    },
  }));
});
...

Cómo agregar más funciones

Ahora que aprendiste los conceptos básicos, puedes mejorar y personalizar tu acción con las APIs específicas de Canvas. En esta sección, se explica cómo implementar estas APIs en tu acción de Interactive Canvas.

sendTextQuery()

El método sendTextQuery() envía consultas de texto a la acción conversacional para invocar un intent de manera programática. En este ejemplo, se usa sendTextQuery() para reiniciar el juego de giro de triángulos cuando el usuario hace clic en un botón. Cuando el usuario hace clic en el botón "Reiniciar el juego", sendTextQuery() llama al intent Restart game y muestra una promesa. Esta promesa da como resultado SUCCESS si el intent está activado y BLOCKED si no lo está. El siguiente fragmento activa el intent y controla los casos de éxito y error de la promesa:

//main.js
...
that.action.canvas.sendTextQuery('Restart game')
    .then((res) => {
      if (res.toUpperCase() === 'SUCCESS') {
        console.log(`Request in flight: ${res}`);
        that.button.texture = that.button.textureButtonDisabled;
        that.sprite.spin = false;
      } else {
        console.log(`Request in flight: ${res}`);
      }
    });
...

Si la promesa da como resultado SUCCESS, el intent Restart game envía un HtmlResponse a tu app web:

//index.js
...
app.intent('restart game', (conv) => {
  conv.ask(new HtmlResponse({
    data: {
      command: 'RESTART_GAME',
    },
...

Este HtmlResponse activa la devolución de llamada onUpdate(), que ejecuta el código en el siguiente fragmento de código RESTART_GAME:

//action.js
...
RESTART_GAME: function(data) {
  that.scene.button.texture = that.scene.button.textureButton;
  that.scene.sprite.spin = true;
  that.scene.sprite.tint = 0x0000FF; // blue
  that.scene.sprite.rotation = 0;
},
...

OnTtsMark()

Se llama a la devolución de llamada OnTtsMark() cuando incluyes una etiqueta <mark> con un nombre único en la respuesta de SSML para el usuario. En los siguientes extractos del ejemplo de muñeco de nieve, OnTtsMark() sincroniza la animación de la app web con el resultado de TTS correspondiente. Cuando la acción le dice al usuario Lo sentimos, perdiste, la app web deletrea la palabra correcta y muestra las letras al usuario.

El intent Game Over Reveal Word incluye una marca personalizada en la respuesta que se envía al usuario cuando perdió el juego:

//index.js
...
app.intent('Game Over Reveal Word', (conv, {word}) => {
  conv.ask(`<speak>Sorry, you lost.<mark name="REVEAL_WORD"/> The word is ${word}.` +
    `${PLAY_AGAIN_INSTRUCTIONS}</speak>`);
  conv.ask(new HtmlResponse());
});
...

Luego, el siguiente fragmento de código registra la devolución de llamada OnTtsMark(), verifica el nombre de la marca y ejecuta la función revealCorrectWord(), que actualiza la app web:

//action.js
...
setCallbacks() {
  const that = this;
  // declare assistant canvas action callbacks
  const callbacks = {
    onTtsMark(markName) {
      if (markName === 'REVEAL_WORD') {
        // display the correct word to the user
        that.revealCorrectWord();
      }
    },
...

Restricciones

Ten en cuenta las siguientes restricciones cuando desarrolles tu app web:

  • No hay cookies
  • Sin almacenamiento local
  • Sin ubicación geográfica
  • Sin uso de la cámara
  • No hay ventanas emergentes.
  • Mantente por debajo del límite de memoria de 200 MB
  • El encabezado de terceros ocupa la parte superior de la pantalla.
  • No se pueden aplicar estilos a los videos
  • Solo se puede usar un elemento multimedia a la vez
  • Sin video HLS
  • No hay base de datos Web SQL
  • No es compatible con la interfaz SpeechRecognition de la API de Web Speech.
  • Sin grabación de audio o video
  • No se aplica la configuración del modo oscuro

Uso compartido de recursos entre orígenes

Debido a que las apps web de Interactive Canvas están alojadas en un iframe y el origen está configurado como nulo, debes habilitar el uso compartido de recursos entre dominios (CORS) para tus servidores web y recursos de almacenamiento. Esto permite que tus recursos acepten solicitudes de orígenes nulos.