Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}

<div
class='heap-player'
...attributes
{{did-insert this.initializePlayer}}
{{will-destroy this.destroyPlayer}}
Copy link
Collaborator Author

@hashicc hashicc Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ember template lint warns against using these

>

</div>
112 changes: 112 additions & 0 deletions ui/admin/app/components/session-recording/player/asciinema-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { later } from '@ember/runloop';
import * as AsciinemaPlayer from 'asciinema-player';

export default class HeapPlayerComponent extends Component {
// =properties

/**
* @type {?AsciinemaPlayer}
*/
@tracked player = null;

/**
*
*/
initialized = false;

/**
* Options of the underlying AsciinemaPlayer supported by this component,
* which may be passed as named arguments to the player.
* @type {string[]}
* @see https://github.com/asciinema/asciinema-player#options
*/
supportedOptions = new Array(
'autoPlay',
'loop',
'startAt',
'speed',
'idleTimeLimit',
'theme',
'poster',
'fit',
'controls',
'markers',
'pauseOnMarkers',
);

/**
* An object of options where each possible key from `supportOptions` is
* included if and only if its associated value was passed to the component
* as an argument.
*
* E.g. `@autoPlay={{true}} @fit='both'` results in
* the options object `{autoPlay: true, fit: 'both'}`.
* @type {object}
* @see https://github.com/asciinema/asciinema-player#options
*/
get options() {
return this.supportedOptions.reduce(
(obj, key) => {
return this.args?.[key] !== undefined
? { ...obj, [key]: this.args[key] }
: obj
},
{}
);
}

// =methods

/**
* Initializes the asciinema player within the current element.
* @param {object|string}
* source - URL or object passed through to AsciinemaPlayer.create(source).
* @param {Element}
* containerElement - DOM element passed from the did-insert modifier.
* @returns {AsciinemaPlayer}
*/
create(source, containerElement, options) {
// cleanup previous player, if any
this.dispose();
// initialize a new AsciinemaPlayer
this.player =
AsciinemaPlayer.create(source, containerElement, options);
return this.player;
}

/**
* Calls asciinema-player's `dispose()` method to destroy the player and
* cleanup the DOM. Unsets this component's `player` property.
*/
dispose() {
this.player?.dispose();
this.player = null;
}

// =actions

/**
* Creates an AsciinemaPlayer within the passed `containerElement`.
*/
@action
initializePlayer(containerElement) {
const { data } = this.args;
this.create({ data }, containerElement, this.options);
}

/**
* Destroys the currently initialized AsciinemaPlayer, if any.
*/
@action
destroyPlayer() {
later(() => this.dispose(), 250);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ember eslint warns against using runloop functions

}
}
1 change: 1 addition & 0 deletions ui/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
},
"dependencies": {
"@babel/runtime": "7.27.6",
"asciinema-player": "^3.12.1",
"ember-named-blocks-polyfill": "^0.2.5",
"lodash": "^4.17.21",
"uuid": "^11.0.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import { module, test } from 'qunit';
import { setupRenderingTest } from 'dummy/tests/helpers';
import { render, waitUntil } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

module('Integration | Component | heap/player', function (hooks) {
setupRenderingTest(hooks);

test('it renders', async function (assert) {
assert.expect(1);

const asciicast = await fetch('/example.cast');
const asciicastContent = await asciicast.text();
this.set('data', asciicastContent);

await render(hbs`<Heap::Player @data={{this.data}} @poster='npt:1:30' />`);
// AsciinemaPlayer does not come with a "ready" event, and its
// initialization is async. Therefore tests must `waitUntil` the expected
// DOM state is reached.
await waitUntil(() =>
assert.dom('.ap-player')
.hasAnyText('ember-asciinema-player git:(main*)')
);
});
});