Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐞 [Website] Add some debugging UI #78

Merged
merged 27 commits into from
Jan 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c2941a5
:boom: [Utilities] supportMatchMedia() is no longer a nested callback
beefchimi Jan 22, 2024
dbe4e15
:label: [Website] useSupported() now expects a boolean callback
beefchimi Jan 22, 2024
9220336
:hook: [Website] New useDebugStore() hook
beefchimi Jan 22, 2024
7df0879
:lipstick: [Styles] Offer a collapsed-border-row--reverse class
beefchimi Jan 22, 2024
f3f3b30
:boom: [StackLabel] Truncation now requires truncate prop
beefchimi Jan 22, 2024
b8eb6cc
:recycle: [StackControls] Now passing truncate to StackLabel
beefchimi Jan 22, 2024
f4d5382
:hook: [useDebugManager] New store hook
beefchimi Jan 22, 2024
bf996e8
:building_construction: [ManagerDebug] New debug section
beefchimi Jan 22, 2024
b3bb909
:recycle: [HomeView] Now conditionally rendering ManagerDebug
beefchimi Jan 22, 2024
a18972c
:books: [Docs] Update contributing.md
beefchimi Jan 23, 2024
ac62d8a
:sparkles: [Earwurm] Add suspend() and resume() methods
beefchimi Jan 23, 2024
6de65c7
:sparkles: [ManagerDebug] Add suspend/resume functionality
beefchimi Jan 23, 2024
c2d56ed
:label: [Earwurm] Add a play event to ManagerEventMap
beefchimi Jan 23, 2024
7287b4c
:boom: [Earwurm] Replace auto resume/suspend with public resume/suspe…
beefchimi Jan 23, 2024
a9458f0
:books: [Docs] Give examples of auto-suspension
beefchimi Jan 23, 2024
abfe58e
:recycle: [useDebugManager] Start listening to the play event
beefchimi Jan 23, 2024
aea6182
:boom: [Earwurm] Remove suspend token and call suspend when library i…
beefchimi Jan 26, 2024
092f0a4
:test_tube: [Earwurm] Update tests
beefchimi Jan 26, 2024
2e8d441
:books: [Docs] Further improve examples.md for suspend examples
beefchimi Jan 26, 2024
f70c3da
:books: [Docs] Update api.md to reflect resume and suspend changes
beefchimi Jan 26, 2024
5d184cb
:speech_balloon: [Helpers] Clarify a comment now that we no longer ha…
beefchimi Jan 26, 2024
c103efc
:bug: [Earwurm] Now resuming audio when suspended and .play() is called
beefchimi Jan 27, 2024
90f45ca
:lipstick: [ManagerDebug] More obvious when new debug items are added
beefchimi Jan 27, 2024
798b5c8
:recycle: [useDebugManager] Better unlock state history
beefchimi Jan 27, 2024
e80417f
:sparkles: [Website] Pause all audio when interrupted
beefchimi Jan 27, 2024
a975680
:memo: [Report] Add changeset
beefchimi Jan 27, 2024
c47319b
:arrow_up: [Dependency] Various bumps
beefchimi Jan 27, 2024
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
5 changes: 5 additions & 0 deletions .changeset/blue-jank-crime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"earwurm": minor
---

Remove timed auto-suspend in favour of exposing `.suspend() / .resume()` methods on `Earuwrm`.
5 changes: 5 additions & 0 deletions .changeset/thick-oranges-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"earwurm": minor
---

New `play` event available on `Earwurm` instance.
4 changes: 2 additions & 2 deletions app/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"type": "module",
"engines": {
"node": ">=20.10.0",
"pnpm": ">=8.14.0"
"pnpm": ">=8.15.0"
},
"scripts": {
"clean": "rm -rf dist && rm -rf *.tsbuildinfo",
Expand All @@ -32,7 +32,7 @@
},
"devDependencies": {
"@earwurm/types": "workspace:*",
"@rushstack/eslint-patch": "^1.7.0",
"@rushstack/eslint-patch": "^1.7.2",
"@tsconfig/node20": "^20.1.2",
"@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-prettier": "^9.0.0",
Expand Down
9 changes: 7 additions & 2 deletions app/website/src/components/StackLabel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import {classNames} from '@/helpers';
export interface StackLabelProps {
label: string;
populated?: boolean;
truncate?: boolean;
}

defineProps<StackLabelProps>();
</script>

<template>
<div :class="classNames('StackLabel', {populated})">
<p class="Text">{{ label }}</p>
<p :class="classNames('Text', {truncate})">{{ label }}</p>
</div>
</template>

Expand All @@ -21,6 +22,7 @@ defineProps<StackLabelProps>();
place-items: center;
place-content: center;
padding: 0.8rem;
flex: 0 0 auto;
min-width: var(--row-height);
height: var(--row-height);
color: var(--color-primary);
Expand All @@ -37,8 +39,11 @@ defineProps<StackLabelProps>();
}

.Text {
font-size: 1.4rem;
}

.truncate {
@apply truncate;
@apply flex-fix-width;
font-size: 1.4rem;
}
</style>
2 changes: 1 addition & 1 deletion app/website/src/hooks/useSupported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {computed} from 'vue';
import {useMounted} from './useMounted';

// Adapted from Vue Use: https://vueuse.org/
export function useSupported(callback: () => unknown) {
export function useSupported(callback: () => boolean) {
const isMounted = useMounted();

return computed(() => {
Expand Down
211 changes: 211 additions & 0 deletions app/website/src/sections/ManagerDebug.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<script setup lang="ts">
import {computed, type ComponentPublicInstance} from 'vue';

import {useDebugManager, useEarwurmStore} from '@/store';
import {IconAction, StackLabel} from '@/components';

const {manager} = useEarwurmStore();
const {
activeStacks,
currentState,
stateHistory,
errorHistory,
unlockHistory,
playHistory,
} = useDebugManager();

const running = computed(() => currentState.value === 'running');

function togglePlayback() {
if (running.value) {
manager.suspend();
} else {
manager.resume();
}
}

function scrollEnd(element: Element | ComponentPublicInstance | null) {
if (element instanceof Element) {
element.scroll({left: 12345, behavior: 'smooth'});
}
}
</script>

<template>
<section class="ManagerDebug collapsed-border-grid">
<div class="collapsed-border-row">
<StackLabel label="Stacks" populated />

<ul
:ref="(el) => scrollEnd(el)"
class="DebugList collapsed-border-row pattern-halftone"
>
<li v-for="stack in activeStacks" :key="stack" class="DebugItem">
<StackLabel :label="stack" />
</li>
</ul>
</div>

<div class="collapsed-border-row">
<StackLabel label="State" populated />

<ul
:ref="(el) => scrollEnd(el)"
class="DebugList collapsed-border-row pattern-halftone"
>
<li
v-for="(state, index) in stateHistory"
:key="`${state}-${index}`"
class="DebugItem"
>
<StackLabel :label="state" />
</li>
</ul>
</div>

<div class="collapsed-border-row">
<StackLabel label="Errors" populated />

<ul
:ref="(el) => scrollEnd(el)"
class="DebugList collapsed-border-row pattern-halftone"
>
<li
v-for="(error, index) in errorHistory"
:key="[...error, index].join(' | ')"
class="DebugItem"
>
<StackLabel :label="error.join(' | ')" />
</li>
</ul>
</div>

<div class="collapsed-border-row">
<StackLabel label="Unlocked" populated />

<ul
:ref="(el) => scrollEnd(el)"
class="DebugList collapsed-border-row pattern-halftone"
>
<li
v-for="(status, index) in unlockHistory"
:key="`Unlocked-${status}-${index}`"
class="DebugItem"
>
<StackLabel :label="status.toString()" />
</li>
</ul>
</div>

<div class="collapsed-border-row">
<StackLabel label="Playing" populated />

<ul
:ref="(el) => scrollEnd(el)"
class="DebugList collapsed-border-row pattern-halftone"
>
<li
v-for="(status, index) in playHistory"
:key="`Playing-${status}-${index}`"
class="DebugItem"
>
<StackLabel :label="status.toString()" />
</li>
</ul>
</div>

<div class="collapsed-border-row">
<IconAction
icon="unmuted"
icon-pressed="muted"
:label="running ? 'Suspend' : 'Resume'"
:a11y="`${running ? 'Suspend' : 'Resume'} the Earwurm`"
:pressed="running"
pressed-style
@action="togglePlayback"
/>
</div>
</section>
</template>

<style scoped>
.ManagerDebug {
@apply flex-fix-width;
position: relative;

/*
* Overlaid border required so that our DebugList
* can scroll behind the border lines.
*/
&::before {
@apply interaction-disable;
@apply position-cover;
z-index: 2;
content: '';
display: block;
box-shadow: inset 0 0 0 var(--app-border-width) var(--color-primary);
}

> .collapsed-border-row {
position: relative;
z-index: 1;
}

> .collapsed-border-row:last-child {
z-index: 3;
}
}

.DebugList {
counter-reset: debug-counter;
flex: 1 1 auto;
box-shadow: inset 0 0 0 var(--app-border-width) var(--color-primary);
overflow-x: scroll;
}

.DebugItem {
counter-increment: debug-counter;
position: relative;

&:first-child {
margin-left: auto;
}

&::before {
content: counter(debug-counter);
position: absolute;
top: 50%;
margin-top: calc(1.6em / 2 * -1);
left: calc(var(--app-border-width) * 2);
display: grid;
place-items: center;
place-content: center;
width: 1.6em;
height: 1.6em;
font-size: 1rem;
color: var(--color-primary);
box-shadow: inset 0 0 0 var(--app-border-width) var(--color-primary);
}

&::after {
@apply interaction-disable;
content: '';
display: block;
position: absolute;
inset: 0;
opacity: 0;
background-color: var(--color-primary);
transition: opacity var(--duration-slow) var(--easing-cubic);
}

& > div {
padding-left: 2rem;
}
}

@starting-style {
.DebugItem::after {
opacity: 1;
}
}
</style>
2 changes: 1 addition & 1 deletion app/website/src/sections/StackControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const preparing = computed(() => hasStack.value && !loaded.value);
<template>
<article :id="`Stack-${id}`" class="StackControls collapsed-border-grid">
<div class="collapsed-border-row">
<StackLabel :label="id" :populated="hasStack && loaded" />
<StackLabel :label="id" :populated="hasStack && loaded" truncate />

<div v-if="hasStack" class="PopulatedBar collapsed-border-row">
<div class="VolumeStackWrapper collapsed-border-row">
Expand Down
11 changes: 10 additions & 1 deletion app/website/src/sections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,13 @@ import ManagerControls from './ManagerControls.vue';
import SoundControls from './SoundControls.vue';
import StackControls from './StackControls.vue';

export {AppHeader, AppFooter, ManagerControls, SoundControls, StackControls};
import ManagerDebug from './ManagerDebug.vue';

export {
AppHeader,
AppFooter,
ManagerControls,
SoundControls,
StackControls,
ManagerDebug,
};
3 changes: 3 additions & 0 deletions app/website/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ export {useThemeStore} from './useThemeStore';

export {useStack} from './useStack';
export {useSound} from './useSound';

export {useDebugStore} from './useDebugStore';
export {useDebugManager} from './useDebugManager';
59 changes: 59 additions & 0 deletions app/website/src/store/useDebugManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {computed, ref} from 'vue';
import {type ManagerEventMap} from 'earwurm';

import {useEarwurmStore} from './useEarwurmStore';

type ErrorResponse = Parameters<ManagerEventMap['error']>[0];

const MAX_HISTORY_LENGTH = 44;

// TODO: Consider enabling a auto-suspension option.
// import {clamp, timeMeasurement} from '@earwurm/utilities';
// const safeAutoSuspend = clamp(0, autoSuspend, timeMeasurement.msPerMin);

const {manager, activeStacks} = useEarwurmStore();

const stateHistoryRef = ref([manager.state]);
const errorHistoryRef = ref<ErrorResponse[]>([]);
const unlockHistoryRef = ref([manager.unlocked]);
const playHistoryRef = ref([manager.playing]);

function updateUnlockHistory() {
const currentLength = unlockHistoryRef.value.length;

if (manager.unlocked === unlockHistoryRef.value[currentLength - 1]) {
return;
}

const newHistory = [...unlockHistoryRef.value, manager.unlocked];
unlockHistoryRef.value = newHistory.slice(MAX_HISTORY_LENGTH * -1);
}

manager.on('state', (current) => {
const newStateHistory = [...stateHistoryRef.value, current];
stateHistoryRef.value = newStateHistory.slice(MAX_HISTORY_LENGTH * -1);

updateUnlockHistory();
});

manager.on('play', (active) => {
const newPlayHistory = [...playHistoryRef.value, active];
playHistoryRef.value = newPlayHistory.slice(MAX_HISTORY_LENGTH * -1);
});

manager.on('error', (response) => {
errorHistoryRef.value = [...errorHistoryRef.value, response];
});

export function useDebugManager() {
return {
activeStacks,
currentState: computed(
() => stateHistoryRef.value[stateHistoryRef.value.length - 1],
),
stateHistory: computed(() => stateHistoryRef.value),
errorHistory: computed(() => errorHistoryRef.value),
unlockHistory: computed(() => unlockHistoryRef.value),
playHistory: computed(() => playHistoryRef.value),
};
}
11 changes: 11 additions & 0 deletions app/website/src/store/useDebugStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {computed} from 'vue';

function getSearchParams() {
const rawParams = window.location.search || '';
return new URLSearchParams(rawParams);
}

export function useDebugStore() {
const params = getSearchParams();
return computed(() => params.has('mode', 'debug'));
}
Loading
Loading