Skip to content
← Public packages

@kentcdodds/invisible-software

Invisible software: a phone-only step-through demo plus a chart of AI agent benchmark progress.

FollowForkOpen app

src/slides/07-muse.html

88 lines · 7.3 KB · HTML
Raw
<section class="slide uses-chat" data-label="7" data-duration="10" data-steps="[[0,0.5],[0.85,4.5],[6.3,6.8],[7.85,8.6]]" data-opener="[1]" data-human="[2,4]">
<style>
@keyframes muse-dot { 0%, 80%, 100% { opacity: 0.3; transform: translateY(0); } 40% { opacity: 1; transform: translateY(-3px); } }
@scope {
	.phone { left: 1150px; top: 102px; transform: scale(1.1); }
	.muse { position: absolute; inset: 0; background: #f7f7f8; font-family: var(--ios); color: #1d1d1f; }
	.m-top { position: absolute; top: 58px; left: 16px; right: 16px; height: 76px; z-index: 5; }
	.m-burger { position: absolute; left: 0; top: 8px; width: 46px; height: 46px; border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgb(0 0 0 / 0.08); display: grid; place-items: center; }
	.m-invite { position: absolute; right: 0; top: 8px; height: 46px; padding: 0 18px; border-radius: 23px; background: #fff; box-shadow: 0 2px 10px rgb(0 0 0 / 0.08); display: grid; place-items: center; font: 500 16px/1 var(--ios); }
	.m-who { position: absolute; left: 50%; top: 0; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; }
	.m-face { width: 44px; height: 44px; border-radius: 50%; overflow: hidden; }
	.m-face img { width: 100%; height: 100%; display: block; }
	.m-name { margin-top: -6px; padding: 5px 11px; border-radius: 13px; background: #fff; box-shadow: 0 2px 8px rgb(0 0 0 / 0.08); font: 600 14px/1 var(--ios); }
	.muse-msgs { position: absolute; left: 0; right: 0; bottom: 162px; padding: 0 16px; display: flex; flex-direction: column; }
	.m-me { align-self: flex-end; max-width: 270px; background: #e3e3e7; border-radius: 20px; padding: 11px 15px; font: 400 17px/1.36 var(--ios); }
	.m-ai { align-self: flex-start; max-width: 300px; background: #ececee; border-radius: 20px; padding: 11px 15px; font: 400 17px/1.38 var(--ios); }
	.dots { display: inline-flex; gap: 5px; padding: 5px 0; }
	.dots i { width: 7px; height: 7px; border-radius: 50%; background: #8e8e93; animation: muse-dot 1.1s ease-in-out infinite; }
	.dots i:nth-child(2) { animation-delay: .15s; } .dots i:nth-child(3) { animation-delay: .3s; }
	.muse-comp { position: absolute; left: 14px; right: 14px; bottom: 98px; min-height: 48px; border-radius: 24px; background: #fff; box-shadow: 0 1px 6px rgb(0 0 0 / 0.07); display: flex; align-items: center; gap: 10px; padding: 6px 8px 6px 14px; }
	.muse-comp .plus { color: #1d1d1f; display: grid; place-items: center; }
	.muse-comp .txt { flex: 1; font: 400 17px/1.36 var(--ios); min-height: 23px; }
	.muse-comp .ph { color: #a1a1a6; }
	.muse-comp .go { width: 34px; height: 34px; border-radius: 50%; color: #8e8e93; display: grid; place-items: center; flex: none; }
	.muse-comp .go.on { background: #1d1d1f; color: #fff; }
	.m-tabs { position: absolute; left: 14px; right: 14px; bottom: 26px; height: 62px; border-radius: 31px; background: #fff; box-shadow: 0 1px 6px rgb(0 0 0 / 0.07); display: flex; align-items: center; justify-content: space-around; padding: 0 8px; color: #3a3a3c; }
	.m-tabs span { width: 50px; height: 50px; border-radius: 50%; display: grid; place-items: center; }
	.m-tabs span.sel { background: #efeff1; }
	.frown { display: inline-block; width: 22px; height: 22px; vertical-align: -4px; }
}
</style>
<div class="phone"><div class="screen">
	<div class="island"></div>
	<div class="muse">
		<div class="statusbar"></div>
		<div class="m-top">
			<span class="m-burger"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9.5h12M6 14.5h12"/></svg></span>
			<div class="m-who"><span class="m-face"><img src="assets/muse-avatar.png" alt=""></span><span class="m-name">Muse</span></div>
			<span class="m-invite">Invite</span>
		</div>
		<div class="muse-msgs">
			<div class="row me" style="--d:3.6s"><div class="m-me">Hey I want to make a change to my booking scheduler.</div></div>
			<div class="row gone" style="--d:4.1s;--e:6.3s"><div class="m-ai"><span class="dots"><i></i><i></i><i></i></span></div></div>
			<div class="row" style="--d:6.4s"><div class="m-ai">Hey Max. I can't get to it. Your scheduler was built in ChatGPT, so we'd have to rebuild it here, integrations and all.</div></div>
		</div>
		<div class="muse-comp">
			<span class="plus"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></svg></span>
			<span class="txt"><span class="ph">Message</span><span class="typed" style="display:none"></span></span>
			<span class="go" id="go"></span>
		</div>
		<div class="m-tabs">
			<span class="sel"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12a8 7 0 1 1 3.5 5.8L4 19l1-3.4A7 7 0 0 1 4 12z"/></svg></span>
			<span><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="3.5" width="14" height="17" rx="2"/><path d="M9 8h6M9 12h6M9 16h4"/></svg></span>
			<span><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18h6M10 21h4M12 3a6 6 0 0 0-3.5 10.9c.6.5 1 1.2 1 2V16h5v-.1c0-.8.4-1.5 1-2A6 6 0 0 0 12 3z"/></svg></span>
			<span><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8.5 12l2.5 2.5 4.5-5"/></svg></span>
			<span><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="7.5" cy="7.5" r="3"/><circle cx="16.5" cy="7.5" r="3"/><circle cx="7.5" cy="16.5" r="3"/><circle cx="16.5" cy="16.5" r="3"/></svg></span>
		</div>
		<div class="home-ind"></div>
	</div>
</div></div>
<script>
deck.slide(({ root, onTime, typer, tapOn }) => {
	const $ = (s) => root.querySelector(s)
	const msg = 'Hey I want to make a change to my booking scheduler.'
	const frown = '<svg class="frown" viewBox="0 0 24 24"><circle cx="12" cy="12" r="11" fill="#f7c948" stroke="#e0a82e" stroke-width="1"/><circle cx="8.3" cy="9.6" r="1.5" fill="#5b3a12"/><circle cx="15.7" cy="9.6" r="1.5" fill="#5b3a12"/><path d="M7.6 17.2c2.4-2.6 6.4-2.6 8.8 0" fill="none" stroke="#5b3a12" stroke-width="1.7" stroke-linecap="round"/></svg>'
	tapOn('.muse-comp', 1.0)
	tapOn('#go', 3.3)
	tapOn('.muse-comp', 8.0)
	const ph = $('.muse-comp .ph'), typed = $('.muse-comp .typed'), go = $('#go')
	typer(typed, msg, 1.3, 28)
	const arrive = [...root.querySelectorAll('.m-top, .muse-comp, .m-tabs')]
	onTime((t) => {
		// Arrival (opener 7.1): the empty chat settles in, then waits for Max.
		const a = easeInOut(prog(t, 0.05, 0.45))
		for (const el of arrive) { el.style.opacity = a; el.style.translate = `0 ${(1 - a) * 8}px` }
		const composing = t >= 1.15 && t < 3.5
		const sad = t >= 8.3
		ph.style.display = composing || sad ? 'none' : ''
		typed.style.display = composing || sad ? '' : 'none'
		typed.classList.toggle('caret', composing || sad)
		if (sad) typed.innerHTML = frown
		const on = (composing && t >= 1.35) || sad
		go.classList.toggle('on', on)
		go.innerHTML = on ? icon.send : icon.mic
			})
})
</script>
</section>