Fun with Clippy.js

Remember Clippy? The (not very) helpful assistant that was a major part of Microsoft Office for numerous years? Sure you do!

These interactive assistants were part of the Microsoft Agent suite of technologies and were incorporated into Windows XP as well. It was also infamously used in BonziBUDDY – the fun and interactive gorilla that also happened to be… spyware.

Whilst these technologies have been long mothballed in favour of more intelligent assistants like Siri and Cortana, Clippy and friends live on in our web browsers in the form of a fun jQuery library called Clippy.js.

Should I use it?

In a real-world project, probably not. It’s out-of-date and requires the use of jQuery. It also loads agent data and sounds across the Internet from an Amazon S3 store. But is it fun? Yes!

By the way, if you’re looking for ways to use Microsoft Agent in your .NET applications, you should look into DoubleAgent instead.

As far as I’m aware, there is a version that has been imported to vanilla JavaScript ES6, but I haven’t experimented with it so far.

Installing and embedding Clippy.js

You can clone or download Clippy.js from its GitHub repository, here. You’ll want to pull the minified JavaScript file and CSS stylesheet from the build directory.

As for jQuery, you can choose to download that also (from their website) but I recommend using their official CDN, or content delivery network. The current version at this time of writing is 3.5.1, and including the minified version reduces the amount of data needed to be downloaded by your web browser.

Don’t use out-of-date versions of jQuery unless you absolutely have to.

First, include the CSS stylesheet and necessary JavaScript files into the <head> tag of your webpage:

<head>

<!-- Include the Clippy.js stylesheet, assuming locally -->
<link rel="stylesheet" type="text/css" href="clippy.css" media="all">

<!-- jQuery -->
<script 
        src="https://code.jquery.com/jquery-3.5.1.min.js" 
        integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" 
        crossorigin="anonymous" 
        defer
></script>

<!-- Clippy.js, assuming locally -->
<script src="clippy.min.js" defer></script>

</head>

If you don’t include the defer attribute on your scripts, then you should insert them into the footer of the webpage to ensure that they only begin loading once your page has finished loading – it’s also important that you include Clippy.js after jQuery.

Usage

You can write your jQuery for playing with your agent in-line or in an external file, but for our examples, let’s keep everything within a <script> tag.

First, you should note that you have a choice of multiple agents that Clippy.js supports, including but not limited to:

  • Clippy – obviously
  • Peedy, the parrot
  • Rover, the dog from Windows XP
  • Bonzi, the purple gorilla

Let’s use Clippy and get it to appear.

<script>
  clippy.load('Clippy', function(agent) {
    agent.show();
    agent.speak('Hello World!');
  });
</script>

With any luck, it should appear and greet us! You can find a full list of available action actions from its aforementioned GitHub repository.

Building a very basic action generator

See the Pen Clippy.js by Oliver Earl (@oliverearl) on CodePen.

We’re going to build a very basic program that constructs an agent and has them go through each of their animations, one by one. You can follow along or take a look at the above CodePen. If it isn’t appearing for you, you can click here to get it.

Let’s add a heading and a paragraph element to our webpage. The paragraph will have an ID that we can target with jQuery later. You can use whatever CSS styling you want – I’ve just replaced the default text font with sans-serif for now.

<main>
  <h1>Current animation:</h1>
  <p id="state">None</p>
</main>

Let’s declare a new function that will take two parameters – an agent and the agent’s animations. I’m assuming absolute basic JavaScript knowledge, so I’ll keep things as simple as I can. Add this underneath the }); of the last block of code, but still before </script>.

In the block before it, let’s use the JavaScript function setTimeout() to call this new function after a few seconds have passed – we will pass our initialised agent and its available animations as arguments, and set a time of 8000 milliseconds – 8 seconds. It should look like this:

<script>
  clippy.load('Clippy', function(agent) {
    agent.show();
    agent.speak('Hello World!');

    setTimeout(animate(agent, agent.animations()), 8000);
  });

  function animate(agent, animations) {
    // Code will go here
  }
</script>

Let’s consider what we need to do by decomposing the problem, we want to:

  • Loop through all of the available animations
  • Have Clippy perform that animation
  • Print out what animation Clippy is performing to the paragraph tag.

In our new function, we can use a jQuery element selector to store our paragraph HTML element in a variable. jQuery variables are typically denoted by a $ sign. We can put this before our loop so that it doesn’t get redeclared on each iteration.

let $currentAnimation = $("#state");

Now let’s consider the loop. We can use a basic For loop to iterate through the animations, as we know exactly how many times we need to loop through (by measuring the length of animations). Within the loop, we can have Clippy perform said animation, and use jQuery to update our paragraph using its built-in text() function.

function animate(agent, animations) {
  let $currentAnimation = $("#state");

  for (let i = 0; i < animations.length; i++) {
    agent.play(animations[i];
    $currentAnimation.text(animations[i]);
  }
}

It looks good! But there’s a problem. Can you work out what it is?

Computers are fast, and Clippy.js uses a queue system to asynchronously perform animations, meaning that the text will update to the very last entry whilst Clippy performs each animation in its queue. We need to slow down this execution using the setTimeout() function that we used earlier:

function animate(agent, animations) {
  let $currentAnimation = $("#state");

  for (let i = 0; i < animations.length; i++) {
    setTimeout(function () {
      agent.play(animations[i]);
      $currentAnimation.text(animations[i]);
    }, i * 8000);
  }
}

Conclusion

And that’s it! I hope that you’ve had a little bit of fun with Clippy.js. Please feel free to fork the CodePen and build something uniquely your own. I hear other developers have managed to embed their own agents inside React or Electron apps, which is both impressive and uniquely horrifying.

Until next time!

Documentation in Laravel 8 with Enlighten

The importance of software documentation, even in an agile world, is undisputed. Keeping documentation, especially for APIs, up-to-date however can be an additional burden on a development team, and thus automatically generated documentation can help supplement this by producing reference materials. This is where Enlighten comes in.

In this guide, we’ll get Laravel Enlighten up and running, which utilises your existing PHPUnit tests (because you should be writing tests) to produce and publish beautiful, customisable documentation, without needing to run any additional commands.

System Requirements

Enlighten does require both PHP 7.4 and Laravel 8. I don’t know whether it works with Lumen. 

If you’re running anything earlier, such as Laravel 6 LTS, I would instead recommend Scribe. For other frameworks or vanilla PHP applications, try phpDocumentor.

You will also need a secondary database. It does not need to be a different kind of database, but it does need to be persistent.

You also need an up-to-date version of Git. This is unlikely to be a problem on macOS, Linux or Windows Subsystem for Linux, but it gave me some teething problems directly running on Windows 10 under Laragon.

Please also note that these are as simple installation instructions as possible. For troubleshooting and optional installation steps, please check the project’s repository.

Installation

Composer installation

Do not use sudo for any of these commands. That will open a whole Pandora’s box you don’t want to deal with.

Using Composer, require Enlighten into your Laravel project using the following command. If you somehow don’t have it, grab it here.

composer require styde/enlighten --dev

Registering the service provider

Next, add the following to your config/app.php file to register Enlighten’s service provider. If you’re not sure where this needs to go, put it underneath the package service providers comment, like thus:

[
  'providers' => [
    // ...
    /*
     * Package Service Providers...
     */
     Styde\Enlighten\Providers\EnlightenServiceProvider::class,
    // ...
  ]
];

Important: Be sure to end the line in a comma, not a semicolon.

Publish assets to the public directory

Run the following command to publish Enlighten’s assets to your public directory. Without this, any documents you generate will lack any CSS formatting or JavaScript.

php artisan vendor:publish --tag=enlighten-build

Integrating Enlighten into your tests

In order for Enlighten to activate during testing and to have access to test data, it will need to be imported and initialised during the setup phase of your tests. You can do this in your TestCase.php abstract class, or in individual test files.

For example:

<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Styde\Enlighten\Tests\EnlightenSetup; // import this trait

abstract class TestCase extends BaseTestCase
{
  use CreatesApplication;
  use EnlightenSetup; // and use said trait here

  protected function setUp(): void
  {
    parent::setUp();

    $this->setUpEnlighten(); // the final piece of the puzzle
  }
}

Important: I believe you need to still call parent::setUp() in your setUp() methods within your tests.

Setting up the secondary database

As aforementioned, Enlighten needs another database to record information. The easiest way to do this is to create another database with the same name of your existing one, except with an _enlighten suffix.

For example, if you have used a MySQL database named laravel, you can create another one called laravel_enlighten which will be used automatically. This assumes you’re using the same database connection, credentials, and driver.

Once you’ve made it, run your migrations to get it set up:

php artisan migrate

Optional: Custom database setup

If you need to use something else, add a new entry to your config/database.php file with the name enlighten. This can support a different driver, credentials, database name, etc. You can add this information as custom entries in your .env file – possibly with an _ENLIGHTEN suffix for clarity.

For example:

'enlighten' => [
  'driver' => 'mysql',
  'host' => env('DB_HOST_ENLIGHTEN', '127.0.0.1')
  'port' => env('DB_PORT_ENLIGHTEN', '3306')
  'database' => env('DB_DATABASE_ENLIGHTEN', 'laravel_enlighten')
  'username' => env('DB_USERNAME_ENLIGHTEN', 'root')
  'password' => env('DB_PASSWORD_ENLIGHTEN', '')
// …
],

I think that you could use an SQLite database in theory, but I haven’t tested this and generally stick to the defaults.

Don’t forget to run your migrations if you choose this route.

Running Enlighten

Once you’ve set everything up, Enlighten will run when you run your test suite:

php artisan test

You can then find your new documentation at the yourwebsite.test/enlighten/ URL.

What’s next?

This guide was designed to get Enlighten up and running as easily as possible. More customisation and configuration options are available to make your documentation as detailed and organised as possible. You can read more on the project’s repository.

I hope that you’ve found this guide useful and I encourage you to check it out with your Laravel API projects! It goes without saying, but Enlighten wasn’t written by me – it’s an open-source project headed by Duilio Palacios and Jeff Ochoa.

Thank you so much for reading.