Cómo construir un complemento de Sketch con JavaScript, HTML y CSS (Parte 1)

Publicado: 2022-03-10
Resumen rápido ↬ Si alguna vez has trabajado con Sketch, lo más probable es que haya habido muchos momentos en los que hayas pensado: "Si tan solo Sketch pudiera hacer esto en particular, sería capaz de lograr la tarea en cuestión". mucho más rápido, más fácil y mejor.” Bueno, ¡no te preocupes más! En este artículo de dos partes, aprenderá cómo crear sus propios complementos de Sketch desde cero, lo que le brindará las habilidades necesarias para resolver exactamente este tipo de problemas.

Este tutorial está destinado a personas que conocen y usan la aplicación Sketch y no tienen miedo de incursionar en el código. Para aprovecharlo al máximo, deberá tener al menos cierta experiencia básica escribiendo JavaScript (y, opcionalmente, HTML/CSS).

El complemento que crearemos se llama "Mosaico". En la primera parte, aprenderemos sobre los archivos básicos que componen un complemento de Sketch; escribiremos algo de JavaScript y crearemos una interfaz de usuario para nuestro complemento con la ayuda de algo de HTML y CSS. El siguiente artículo tratará sobre cómo conectar la interfaz de usuario al código principal del complemento, cómo implementar las funciones principales del complemento y, al final, también aprenderá cómo optimizar el código y la forma en que funciona el complemento.

También compartiré el código del complemento (JS, HTML, CSS) y los archivos que podrá examinar y usar con fines de aprendizaje.

¿Qué son los complementos de Sketch y cómo funcionan?

En Sketch, los complementos son una forma de agregar características y funcionalidades que no están presentes en Sketch "listas para usar". Teniendo en cuenta que casi siempre faltará alguna característica o integración en cualquier programa dado (¡especialmente dada la gran cantidad de necesidades que podría tener cualquier diseñador individual!), Uno puede comenzar a imaginar cómo los complementos pueden ser especialmente útiles y poderosos. Los complementos de boceto pueden hacer prácticamente todo lo que espera, como manipular el color, la forma, el tamaño, el orden, el estilo, la agrupación y los efectos de las capas, pero también pueden hacer cosas como solicitar recursos de Internet, presentar un usuario interfaz, y mucho, mucho más!

En el lado de la programación, todos los complementos de Sketch están escritos en código JavaScript. Bueno, en realidad, eso no es del todo cierto. Es más exacto decir que la mayoría de los complementos de Sketch están escritos en JavaScript, ya que también es posible escribir un complemento de Sketch en uno de los lenguajes de programación de Apple, Objective-C y Swift, aunque incluso requieren una pequeña cantidad de conocimiento de JavaScript.

Pero no te preocupes. En este artículo, nos centraremos en cómo crear complementos de Sketch usando solo JavaScript, HTML y CSS. No repasaremos los conceptos básicos de HTML, CSS o JavaScript; este artículo asume al menos algún conocimiento y experiencia con estos tres. El sitio web para desarrolladores de MDN proporciona un excelente lugar para obtener más información sobre el desarrollo web.

¡Más después del salto! Continúe leyendo a continuación ↓

¡Empecemos!

En primer lugar, ¿qué estamos haciendo?

En este tutorial, le enseñaré cómo crear un complemento básico para principiantes que podrá crear, duplicar y modificar capas, así como presentar al usuario una interfaz de usuario agradable. Al hacerlo, mi objetivo es establecer un conocimiento fundamental sobre el cual pueda desarrollar y utilizarlo para crear sus propios complementos.

El complemento que construiremos se llama Mosaic y es efectivamente un "generador de patrones". Aliméntelo con sus capas, modifique algunas configuraciones y creará un patrón:

Imagen que muestra la interfaz de usuario del complemento Mosaic y algunos patrones de ejemplo.
La interfaz de usuario de Mosaic y algunos ejemplos de patrones creados con ella. (Vista previa grande)

Si desea instalar y jugar con Mosaic, puede descargar el complemento completo desde GitHub.

Un poco de historia: Mosaic está inspirado en gran parte en un complemento de Adobe Fireworks de la vieja escuela llamado Twist-and-Fade . Twist-and-Fade era bastante poderoso, capaz de duplicar una capa cualquier cantidad de veces mientras ajustaba su tono, posición, rotación, tamaño y opacidad. El complemento incluso pudo generar GIF animados, como este, donde creó los marcos para los dos elementos giratorios en la cinta de casete:

Imagen que muestra una cinta de casete de música con tambores giratorios
Cinta de casete animada (fuente). (Vista previa grande)

(Aquí hay un video que muestra Twist and Fade si está interesado en ver exactamente cómo funcionó).

A los efectos de este tutorial, crearemos un complemento algo similar para Sketch, aunque simplificado intencionalmente para mantener el tutorial lo más accesible posible. Específicamente, nuestro complemento podrá:

  • Duplique cualquier capa de Sketch (mapa de bits o vector) y modifique la posición, rotación y opacidad de la capa duplicada. Esto nos dará una introducción a la manipulación de capas utilizando las API de JavaScript de Sketch.
  • Muestre una interfaz de usuario creada con HTML, CSS y JS, que le enseñará cómo crear fácilmente una interfaz para el complemento, mediante el uso de tecnologías web con las que quizás ya esté familiarizado. La interfaz del complemento es bastante importante, ya que es la forma en que recopilaremos las entradas del usuario con respecto a cómo el usuario quiere que se vea la imagen de mosaico resultante.

Creando nuestro complemento base en diez segundos planos

Primero, crearemos la "base" (o plantilla) para el complemento que queremos construir. Podríamos crear todos los archivos y carpetas necesarios que componen un complemento manualmente, pero afortunadamente no tenemos que hacerlo, porque Sketch puede hacerlo por nosotros. Una vez que hayamos generado el complemento de plantilla, podremos personalizarlo como mejor nos parezca.

Hay una técnica realmente rápida y fácil que podemos usar para crear el complemento de plantilla, que es prácticamente mi método de acceso cuando necesito combinar un complemento para resolver cualquier problema con el que esté lidiando en un momento dado. Así es como funciona:

Con Sketch abierto, verifique la barra de menú en la parte superior de la pantalla y haga clic en Plugins -> Run Script . Esto abrirá un cuadro de diálogo que podemos usar para probar y ejecutar el código. También podemos guardar cualquier código que ingresemos en él como un complemento, que es la parte que nos interesa específicamente en este momento.

Borre cualquier código que ya esté en este cuadro de diálogo y reemplácelo con el siguiente código de demostración:

 const UI = require("sketch/ui"); UI.message(" Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!");

Luego, presione Save Script as Plugin en la parte inferior izquierda de la ventana, ingrese el nombre que le gustaría que tenga este complemento (en nuestro caso, esto es "Mosaico"), luego Save Script as Plugin una vez más.

Presione "Guardar" en la parte inferior izquierda de la ventana e ingrese el nombre que desea que tenga este complemento. (Vista previa grande)

Lo creas o no, ya hemos terminado, todo lo que queda es comer el pastel que acabamos de hornear. Aquí viene la parte divertida. Al abrir el menú Complementos una vez más, debería ver algo como esto: ¡su nuevo complemento aparece como "Mosaico"! ¡Haz click en eso!

(Vista previa grande)

¡Felicitaciones, acabas de escribir tu primer complemento de Sketch!

Lo que debería ver después de hacer clic en "Mosaico" debería ser como el video corto de arriba, con un mensaje de información sobre herramientas discreto que aparece en la parte inferior de la pantalla que comienza con las palabras "Hola...", que es exactamente lo que dice el código que pegamos. que hacer. Esto es lo que hace que esta técnica sea tan excelente: facilita pegar, modificar y probar el código sin tener que crear un complemento desde cero. Si está familiarizado o alguna vez ha jugado con la consola web de su navegador, esto es básicamente eso. Tener esta herramienta en su bolsillo trasero mientras construye y prueba el código es imprescindible.

Hagamos un resumen rápido de lo que hace el código que agregó:

Primero, importa el módulo sketch/ui de la biblioteca JS integrada de Sketch y lo asigna a la variable UI . Este módulo contiene un par de métodos útiles relacionados con la interfaz, uno de los cuales usaremos:

 const UI = require("sketch/ui");

A continuación, llama al método de message (que es parte del módulo sketch/ui ) con la cadena de texto que queremos que se muestre en la información sobre herramientas que vimos:

 UI.message(" Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!");

El método message() proporciona una excelente manera de presentar un mensaje discreto al usuario; es ideal para los casos en los que no necesita robar el foco (no modal) y no necesita botones o campos de texto sofisticados. También hay otras formas de presentar elementos comunes de la interfaz de usuario, como alertas, avisos y demás, algunos de los cuales usaremos a medida que construimos Mosaic.

Personalización de los metadatos de nuestro complemento

Ahora tenemos un complemento básico para empezar, pero aún tenemos que modificarlo más y hacerlo realmente nuestro. Nuestro siguiente paso será cambiar los metadatos del complemento.

Para este paso, necesitaremos echar un vistazo a lo que se llama el paquete de complementos . Cuando presionó Guardar en la ventana 'Ejecutar secuencia de comandos', Sketch guardó su complemento como una carpeta llamada Mosaic.sketchplugin que puede encontrar en el directorio ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins . Eso es un poco largo y molesto de recordar; como acceso directo, también puede abrirlo a través de Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder . Aunque aparece en Finder como un solo archivo, en realidad es una carpeta que contiene todo lo que nuestro complemento necesita para que Sketch lo ejecute. La razón por la que aparece como un solo archivo a pesar de ser una carpeta es porque cuando instaló Sketch por primera vez, Sketch registró la extensión .sketchplugin como un "paquete" (un tipo especial de carpeta que aparece como un archivo) y solicitó que se abriera automáticamente. en Sketch cuando se abre.

Echemos un vistazo al interior. Haga clic derecho en Mosaic.sketchplugin , luego haga clic en "Mostrar contenido del paquete". En el interior, debería ver la siguiente estructura de directorios:

 Contents/ └ Resources/ └ Sketch/ └ manifest.json └ script.cocoascript

Quizás se pregunte por qué hay un archivo con la extensión .cocoascript . No se preocupe, es solo un archivo JavaScript normal y solo contiene el código que ingresamos anteriormente. Continúe y cambie el nombre de este archivo a index.js , lo que cambiará la estructura del directorio para que se vea como el siguiente:

 Contents/ └ Resources/ └ Sketch/ └ manifest.json └ index.js

La forma más común de organizar los archivos dentro de un paquete de complementos es la siguiente: su código (JavaScript) y manifest.json pertenecen a Sketch/ , y los recursos (piense en imágenes, archivos de audio, archivos de texto, etc.) pertenecen a Resources/ .

Comencemos modificando el archivo llamado manifest.json . Ábralo dentro de su editor de código favorito, como Visual Studio Code o Atom.

Verás que por el momento hay relativamente poco aquí dentro, pero pronto agregaremos más. El manifiesto del complemento sirve principalmente para dos propósitos:

  1. Primero, proporciona metadatos que describen el complemento para el usuario, como su nombre, versión, el nombre del autor, etc. Sketch utiliza esta información en el cuadro de diálogo Sketch -> Preferences -> Plugins para crear una lista y una descripción para su complemento.
  2. En segundo lugar, también le dice a Sketch cómo llegar a su negocio; es decir, le dice a Sketch cómo le gustaría que se vea el menú de su complemento, qué teclas de acceso rápido asignar a su complemento y dónde se encuentra el código de su complemento (para que Sketch pueda ejecutarlo).

Teniendo en cuenta el propósito n.º 1, describir el complemento al usuario, probablemente notará que en este momento no se proporciona una descripción ni un autor, lo que sería confuso para el usuario y dificultaría la identificación del complemento. Arreglemos eso ajustando los valores de las claves relevantes a:

 { "description": "Generate awesome designs and repeating patterns from your layers!", "author": "=> Your name here <=" }

A continuación, ajustemos el identificador del complemento. Este identificador utiliza lo que se llama una "notación de dominio inverso", que es una forma muy concisa (o aburrida, elija) de decir "tome el dominio de su sitio, invierta el orden y luego coloque el nombre de su producto al final". Esto saldrá algo como: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct .

No tienes que apegarte a esta convención de nomenclatura: puedes poner lo que quieras aquí, siempre que sea lo suficientemente único para evitar conflictos con otros complementos (aunque probablemente sea una buena idea apegarse al formato RDN, especialmente porque proporciona un sistema simple y reutilizable para los identificadores de sus complementos).

Para ello, cambia tu identificador a com.your-name.mosaic :

 { "identifier": "com.your-name.mosaic" }

Personalmente, me gusta tomar todas las claves relacionadas con los metadatos (título, autor, identificador, etc.) y agruparlas cerca de la parte superior del archivo de manifiesto para que no estén esparcidas por todos lados y ayudar a preservar mi cordura cuando necesito encontrarlas. .

A continuación, echemos un vistazo a las teclas de menu y commands . Estos dos son responsables de decirle a Sketch qué código llamar y en respuesta a qué.

Si observa la tecla de menu , verá que contiene una tecla de title , cuyo valor es el nombre con el que aparecerá nuestro complemento en el menú Plugins . También tiene una clave de items , que es una lista de identificadores de comandos :

 { "menu": { "title": "Mosaic", "items": [ "com.bohemiancoding.sketch.runscriptidentifier" ] } }

En este momento, solo hay un identificador de comando en esta lista, "com.bohemiancoding.sketch.runscriptidentifier" . Los identificadores de comandos siempre apuntan a un comando en la lista de commands . En este momento nuestro complemento solo tiene un comando, que es el que tiene este identificador:

 { "commands": [ { "script" : "script.cocoascript", "name" : "Mosaic", "handlers" : { "run" : "onRun" }, "identifier" : "com.bohemiancoding.sketch.runscriptidentifier" } ] }

Cada vez que agregue un identificador de comando a una entrada de menu , Sketch buscará la entrada de comando que tiene ese identificador y mostrará el valor de su clave de name (que en este caso es "Mosaico") y lo mostrará en el menú de su complemento. del identificador.

En cuanto al papel que juegan los comandos, podemos pensar en una entrada de comando como una forma de decirle a Sketch qué función en el código JavaScript de nuestro complemento queremos ejecutar cuando se invoque ese comando, la "invocación" generalmente es el clic del usuario en el menú asociado. ít. La entrada del comando no hace nada por sí sola, es solo JSON: simplemente proporciona una descripción para Sketch de dónde buscar el JavaScript que necesita ejecutar cuando se invoca el comando.

Hasta ahora, hemos hablado sobre lo que hacen el name de un comando y las claves de identifier , pero hay otras dos claves en un comando que deben abordarse: script y handlers .

La clave del script le dice a Sketch dónde está el archivo JavaScript que debe ejecutar. Observe cómo Sketch asume que el archivo de secuencia de comandos en cuestión está en la carpeta Sketch/ , por lo que, para simplificar, querrá asegurarse de que todo su código JavaScript se encuentre en algún lugar de la carpeta Sketch/ . Antes de pasar de esta clave , es importante que se asegure de cambiar el valor de esta clave a index.js , tal como cambiamos el nombre del archivo anteriormente. De lo contrario, Sketch no podrá encontrar ni ejecutar su archivo JavaScript.

El valor de la clave de los handlers es lo que Sketch busca para determinar qué función en su JavaScript llamar. Aquí, solo tenemos un conjunto de controladores: run , con el valor onRun . run es el nombre de una acción de Sketch integrada y predefinida. Esta acción run siempre se llamará cuando un usuario haga clic en un elemento de menú que haga referencia a este comando. onRun es el nombre de una función en el archivo script.cocoascript generado automáticamente (que cambiamos de nombre a index.js ), y la función que queremos que se llame cuando ocurra el evento de run , es decir, cuando el usuario haga clic en el elemento del menú.

En el ejemplo que tenemos hasta ahora, este proceso se desarrolla de la siguiente manera:

  1. El usuario hace clic en nuestro elemento de menú.
  2. Sketch encuentra el comando asociado con ese elemento de menú.
  3. Sketch encuentra el archivo de script al que hace referencia el comando y lo ejecuta (lo que en este caso significa que ejecuta JavaScript en index.js ).
  4. Dado que este comando se invocó al hacer clic en un elemento de menú, se considera una acción de run . Eso significa que Sketch buscará el valor handlers.run del comando para que la función llame a continuación, que en este caso es onRun .
  5. Sketch llama a la función onRun .

Los comandos se llaman más comúnmente en respuesta a un usuario que hace clic en uno de los elementos del menú, pero también se pueden llamar en respuesta a otras acciones del usuario, como que el usuario cambie la selección o una propiedad en una capa. Sin embargo, para este complemento, no usaremos ninguna de estas otras acciones. (Puede obtener más información sobre las acciones y cómo funcionan en la página de ayuda de la API de acciones).

Antes de continuar con este manifiesto, queremos hacer otros dos ajustes. Ahora mismo, nuestro menú tiene la estructura:

 Mosaic └ Mosaic 
Imagen que muestra el elemento del menú Mosaic anidado de forma redundante dentro de otro menú llamado Mosaic
Bastante redundante, ¿verdad? (Vista previa grande)

…lo cual es un poco redundante ya que nuestro complemento solo tiene un elemento de menú. También agrega un poco de fricción innecesaria para nuestro usuario, ya que nuestro complemento ahora requiere dos clics para invocar en lugar de uno. Podemos arreglar esto agregando isRoot: true a nuestro menu :

 { "menu": { "title" : "Mosaic", "items" : [ "com.bohemiancoding.sketch.runscriptidentifier" ], "isRoot": true } }

Esto le indica a Sketch que coloque el primer nivel de elementos del menú directamente debajo del menú Plugins , en lugar de anidarlos debajo del title del menú.

Presiona guardar y regresa a Sketch. Debería ver que ahora Mosaic -> Mosaic ha sido reemplazado solo por Mosaic , ¡perfecto!

Imagen que muestra la interfaz de usuario del complemento Mosaic
Interfaz de usuario de Mosaic. (Vista previa grande)

En cuanto a nuestro segundo ajuste, avancemos y cambiemos el nombre de este identificador de comando a algo menos difícil de manejar. Dado que los identificadores de comandos solo necesitan ser únicos dentro del contexto de un complemento individual, podemos renombrarlo con seguridad a algo más conciso y obvio, como "open" :

 { "commands": [ { ... "identifier" : "open" } ], "menu": { ... "items" : [ "open" ] } }

Antes de continuar, es útil notar que los menús también pueden contener otros menús. Puede crear fácilmente un submenú anidando otra entrada { title: ..., items: ... } dentro de la lista de items de otro menú:

 { "menu": { "title" : "Mosaic", "items" : [ "open", { "title" : "I'm a sub-menu!", "items" : [ "another-command-identifier" ] } ] } }

Creación de la interfaz de usuario del complemento

Hasta ahora, hemos escrito un código de demostración y hemos personalizado el manifiesto de nuestro complemento. Ahora pasaremos a crear su interfaz de usuario, que es esencialmente una página web incrustada en una ventana (similar a los navegadores con los que está familiarizado):

La ventana del complemento. (Vista previa grande)
Imagen que muestra los componentes que componen la interfaz de nuestro complemento: ventana y vista web
Los componentes que componen nuestro plugin. (Vista previa grande)

La ventana

El diseño de la interfaz de usuario de Mosaic tiene su propia ventana, que podemos considerar el componente más básico; vamos a empezar con él. Para crear y mostrar una ventana, tendremos que hacer uso de una clase que está integrada en macOS de forma predeterminada, llamada NSWindow . Durante el resto de este tutorial, en realidad lo haremos un poco (usando API integradas como NSWindow ), lo que puede parecer un poco desalentador si no está familiarizado con él, pero no se preocupe, lo explicaré. todo en el camino!

Nota: Si bien estamos hablando de las API integradas, la razón por la que podemos usar esta clase es gracias a un puente presente en el tiempo de ejecución de JavaScript que usan los complementos de Sketch. Este puente importa automáticamente estas clases, métodos y funciones integradas que normalmente solo estarían disponibles para las aplicaciones nativas.

Abra Sketch/index.js en su editor de código, elimine lo que ya está allí y pegue lo siguiente:

 function onRun(context){ const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.releasedWhenClosed = false; window.makeKeyAndOrderFront(nil); };

Echemos un vistazo a lo que hace este primer bit de código:

 function onRun(context){

¿Recuerdas antes cuando hablamos sobre los comandos y cómo funcionan, y le dijimos a Sketch que llamara en respuesta a un clic en el menú llamado onRun ? (Si necesita un repaso, vuelva a visitar esa parte anterior, luego regrese). Todo lo que hace este bit es crear esa función. También notará que nuestra función onRun toma un argumento de context . Este es un argumento con el que Sketch llamará a sus controladores de comandos que pueden proporcionarnos cierta información. Más adelante, lo usaremos para obtener la URL de nuestro paquete de complementos en la computadora del usuario.

 const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false );

Aquí en realidad estamos haciendo algunas cosas:

  1. Primero, llamamos a alloc() en NSWindow ; esto básicamente significa "reservar algo de memoria para una instancia de NSWindow". Es suficiente saber que tendrá que hacer esto para cada instancia de una clase nativa que desee crear. El método alloc está disponible en todas las clases nativas.
  2. A continuación, llamamos al método de inicialización de NSWindow (es decir, el método que realmente crea una instancia de NSWindow ), que se llama initWithContentRect:styleMask:backing:defer: . Notará que es diferente de lo que llamamos en nuestro código anterior: tiene un montón de dos puntos ( : entre cada argumento. Como no podemos usar esa sintaxis en JavaScript, Sketch convenientemente le cambia el nombre a algo que realmente podamos usar reemplazando los dos puntos con guiones bajos, que es como obtenemos su nombre JS: initWithContentRect_styleMask_backing_defer .
  3. A continuación, pasamos cada uno de los argumentos que necesita el método. Para el primer argumento, contentRect , proporcionamos un rectángulo con un tamaño lo suficientemente grande para nuestra interfaz de usuario.
  4. Para styleMask , usamos una máscara de bits que dice que queremos que nuestra ventana tenga un botón de cierre, una barra de título y que se pueda cambiar el tamaño.
  5. Los siguientes dos argumentos, backing y defer , siempre se establecerán en NSBackingStoreBuffered y false , por lo que realmente no tenemos que preocuparnos por ellos. (La documentación de este método entra en más detalles sobre por qué sucede esto).
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

Aquí establecemos la propiedad NSWindow de releasedWhenClosed en false , lo que significa: “¡Oye! no borre esta ventana de la memoria solo porque el usuario la cierra”. Luego llamamos a makeKeyAndOrderFront (null) , lo que significa: "Mueve esta ventana al frente y dale el foco del teclado".

Vista web: la interfaz

Para facilitar las cosas, ya he escrito el código HTML y CSS de la interfaz de usuario web del complemento que vamos a utilizar; el único código restante que tendremos que agregarle se ocupará de asegurarnos de que podamos comunicarnos entre él y nuestro código de complemento de Sketch.

A continuación, descargue el código HTML y CSS. Una vez que lo haya descargado, extráigalo, luego mueva la carpeta llamada "web-ui" a la carpeta de recursos de nuestro complemento.

Nota : Escribir y optimizar el código HTML/CSS real está fuera del alcance de este tutorial, ya que se centra en JavaScript, que potencia las funciones principales del complemento; pero hay un montón de tutoriales en la web sobre este tema, si desea obtener más información.

Si ejecuta nuestro complemento ahora, verá que muestra una ventana: ¡sí, progreso! Pero está vacío, sin título, y aún no es particularmente útil. Necesitamos que muestre nuestra interfaz web. Para hacerlo, necesitaremos usar otra clase nativa, WKWebView , que es una vista creada específicamente para mostrar contenido web.

Agregaremos el código necesario para crear nuestro WKWebView debajo del código que escribimos para nuestra ventana:

 function onRun(context){ // Create window const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.releasedWhenClosed = false; // Create web view, and set it as the view for our window to display const webView = WKWebView.alloc().init(); window.contentView = webView; // Load our UI into the web view const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/"); const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html"); webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL); // Make window key and move to front window.makeKeyAndOrderFront(nil); };

Si ejecutamos nuestro complemento ahora, veremos que ahora tenemos una ventana abierta que muestra nuestra interfaz de usuario web. ¡Éxito!

Nuevamente, antes de continuar, examinemos qué hace el código que agregamos:

 const webView = WKWebView.alloc().init();

Esto debería parecer familiar: es básicamente lo mismo que hicimos cuando creamos nuestra NSWindow : asignar memoria para una vista web y luego inicializarla.

 window.contentView = webView;

Esta línea de código le dice a nuestra ventana que muestre la vista web que acabamos de crear.

 const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/");

Aquí nuestro objetivo es crear una URL que apunte a la carpeta web-ui que creamos anteriormente. Para obtener esa URL, necesitamos alguna forma de averiguar dónde se encuentra el paquete de nuestro complemento en el sistema de archivos del usuario. Aquí usamos la propiedad context.scriptURL , que nos da la URL del script que se está ejecutando actualmente . Sin embargo, esto no nos da una String de JavaScript como cabría esperar, sino una instancia de una clase nativa, NSURL , que tiene algunos métodos que facilitan la manipulación de cadenas de URL.

Necesitamos convertir lo que context.scriptURL nos da —

 file://path-to-your-plugin/Contents/Sketch/index.js

- dentro:

 file://path-to-your-plugin/Contents/Resources/web-ui/

Paso a paso:

  1. Llamar URLByDeletingLastPathComponent() la primera vez nos da file://path-to-your-plugin/Contents/Sketch/
  2. Llamar URLByDeletingLastPathComponent() nuevamente nos da file://path-to-your-plugin/Contents/
  3. Y, por último, agregar Resources/web-ui/ al final usando URLByAppendingPathComponent ("Resources/web-ui/") nos da file://path-to-your-plugin/Contents/Resources/web-ui/

También necesitamos crear una segunda URL que apunte directamente al archivo index.html :

 const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html");

Finalmente, le decimos a nuestra vista web que cargue index.html y le demos acceso al contenido de la carpeta web-ui :

 webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

Bien. Hasta ahora, tenemos una ventana que muestra nuestra interfaz de usuario web, tal como queríamos. Sin embargo, aún no está completo: nuestro diseño original no tiene una barra de título (o "chrome"), pero nuestra ventana actual sí. También está el hecho de que cuando hacemos clic dentro de un documento de Sketch, ese documento se mueve frente a nuestra ventana, que no es lo que queremos: queremos que el usuario pueda interactuar con la ventana del complemento y el documento de Sketch sin tener que hacerlo. reenfoca constantemente de una ventana a la otra.

Para solucionar esto, primero debemos deshacernos del cromo de ventana predeterminado y mantener solo los botones. Agregar las dos líneas de código a continuación eliminará la barra de título.

Nota: Al igual que antes, todas las propiedades y métodos que usamos a continuación están documentados en la página de documentación de NSWindow .

 window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden;

Estas dos líneas de código siguientes eliminarán los botones de ventana (también conocidos como "semáforos" en la jerga de MacOS) que no necesitamos: "acercar" y "minimizar", dejando solo el botón "cerrar":

 window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;

Mientras estamos en eso, también avancemos y cambiemos el color de fondo de la ventana para que coincida con el de nuestra interfaz de usuario web:

 window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);

A continuación, debemos hacer algo para mantener nuestra ventana de complemento flotante encima de otras ventanas, de modo que el usuario pueda interactuar con sus documentos de Sketch sin tener que preocuparse de que desaparezca la ventana de Mosaic. Podemos usar un tipo especial de NSWindow para esto, llamado NSPanel , que es capaz de “estar al tanto” de otras ventanas. Todo lo que se necesita para esto es cambiar NSWindow a NSPanel , que es un cambio de código de una sola línea:

 const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Ahora le decimos a la ventana de nuestro panel que flote (permanezca por encima de todas las demás), y solo tome el foco del teclado/ratón cuando sea necesario:

 window.floatingPanel = true; window.becomesKeyOnlyIfNeeded = true;

También podemos ajustar nuestra ventana para que se vuelva a abrir automáticamente en la última posición en la que estaba:

 window.frameAutosaveName = "mosaic-panel-frame";

Esta línea básicamente dice "recuerde la posición de esta ventana guardándola con las preferencias de Sketch bajo la clave mosaic-panel-frame ".

Todos juntos, ahora tenemos el siguiente código:

 function onRun(context){ // Create window const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.becomesKeyOnlyIfNeeded = true; window.floatingPanel = true; window.frameAutosaveName = "mosaic-panel-frame"; window.releasedWhenClosed = false; window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true; window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden; window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1); // Create web view, and set it as the view for our window to display const webView = WKWebView.alloc().init(); window.contentView = webView; // Load our UI into the webview const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/"); const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html"); webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL); // Make window key and move to front window.makeKeyAndOrderFront(nil); };

Organizar el código

Antes de pasar a la siguiente parte, es una buena idea organizar nuestro código para que sea más fácil de navegar y modificar. Dado que todavía tenemos mucho más código para agregar y queremos evitar que index.js se convierta en un vertedero desordenado para todo nuestro código, dividamos un poco las cosas y muevamos nuestro código específico de UI a un archivo llamado ui.js , en la carpeta Sketch . También extraeremos algunas de las tareas de IU que hacemos, como crear la vista web y la ventana, en sus propias funciones.

Cree un nuevo archivo llamado ui.js e inserte el siguiente código en su interior:

 // Private var _window; function createWebView(pageURL){ const webView = WKWebView.alloc().init(); webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; }; function createWindow(){ const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 420, 646), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.becomesKeyOnlyIfNeeded = true; window.floatingPanel = true; window.frameAutosaveName = "mosaic-panel-frame"; window.releasedWhenClosed = false; window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true; window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden; window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1); return window; }; function showWindow(window){ window.makeKeyAndOrderFront(nil); }; // Public function loadAndShow(baseURL){ if(_window){ showWindow(_window); return; } const pageURL = baseURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/index.html"); const window = createWindow(); const webView = createWebView(pageURL); window.contentView = webView; _window = window; showWindow(_window); }; function cleanup(){ if(_window){ _window.orderOut(nil); _window = null; } }; // Export module.exports = { loadAndShow, cleanup };

Hay un par de cambios clave que hicimos aquí que es importante tener en cuenta. Además del hecho de que hemos creado funciones específicas para la creación, ocultación y visualización de nuestra ventana y su vista web, también hemos modularizado el código de nuestra interfaz de usuario.

¿Observe la module.exports = { loadAndShow, cleanup } en la parte inferior? Esta es una forma de especificar exactamente qué objetos y funciones pueden usar los scripts que importan este código de interfaz de usuario (y ocultar aquellos de los que no queremos que se preocupen), lo que significa que ahora tenemos una API más organizada para interactuar. mostrando y destruyendo nuestra interfaz de usuario.

Lectura recomendada : Cómo liberar todo el potencial de los símbolos en Sketch

Veamos cómo se ve esto en la práctica. De vuelta en index.js , elimine el código anterior y agregue lo siguiente:

 const UI = require("./ui"); function onRun(context){ UI.loadAndShow(context.scriptURL); };

Estamos utilizando una función especial que Sketch pone a nuestra disposición automáticamente, require , para importar nuestro código ui.js y asignar el módulo devuelto a la variable UI . Esto nos da acceso a una API simplificada para activar nuestra interfaz de usuario. ¡Las cosas están mucho más ordenadas ahora y son fáciles de encontrar!

Conclusión

Bien hecho, ¡has llegado lejos! In the next part of this tutorial, we'll give our web UI the ability to send us a message when the “Apply” button is clicked, and we'll focus on the main plugin functionality: actually generating layer mosaics!