Files
agency/.pnpm-store/v3/files/be/46336ee96067d0ac984f8ac2eb0d37c59b1b3e8c8278bb792f0552a2d04503d7dd665f688d003f9ff4a0e35285efd6168fef156616d39eff4eedf87f7c3392
hanzo-agent 5af6960b87 run project
Run: sess_f37640604ae254cb4d11dc6217f7b721
2026-09-30 00:04:56 +00:00

84 lines
4.8 KiB
Plaintext

import { getMenuBarSVG, MARK_BLOCKS, MARK_SHADE } from './logos.js';
/**
* Brand motion — the shared shell animation for the Hanzo mark, pure CSS:
*
* 1. intro flip — mark flips in (rotateY 180° → 0, scale .6 → 1, fade)
* 2. idle breathe — a slow, subtle scale pulse once at rest
* 3. wordmark — slides in after the flip, holds, then collapses;
* returns on :hover of the shell
*
* All reduced-motion-safe. Framework-free: pair `MOTION_CSS` with
* `getMotionHTML()` from React, Svelte, Tamagui, or plain HTML — the mark
* and wordmark inherit `currentColor`, so theming is one `color` away.
*/
export const MOTION_CSS = `.hanzo-motion{display:inline-flex;align-items:center;color:inherit}
.hanzo-motion .hanzo-mark{display:block;line-height:0;width:1.25em;height:1.25em;animation:hanzo-flip .5s cubic-bezier(.16,1,.3,1) .2s backwards,hanzo-breathe 6s ease-in-out 3.2s infinite}
.hanzo-motion .hanzo-mark svg{display:block;width:100%;height:100%}
.hanzo-motion .hanzo-word{overflow:hidden;display:inline-block;margin-left:.5em}
.hanzo-motion .hanzo-word>span{display:inline-block;white-space:nowrap;font-weight:600;transform:translateX(-102%);transition:transform .3s ease-out;animation:hanzo-word 2.8s ease-out}
.hanzo-motion:hover .hanzo-word>span{transform:translateX(0)}
@keyframes hanzo-flip{from{opacity:0;transform:perspective(320px) rotateY(180deg) scale(.6)}to{opacity:1;transform:none}}
@keyframes hanzo-breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.04)}}
@keyframes hanzo-word{0%,40%{transform:translateX(-102%)}55%,82%{transform:translateX(0)}100%{transform:translateX(-102%)}}
@media (prefers-reduced-motion:reduce){.hanzo-motion .hanzo-mark,.hanzo-motion .hanzo-word>span{animation:none!important}.hanzo-motion .hanzo-word>span{transition:none}}`;
/**
* The motion shell markup — mark + collapsing wordmark. Inline it (with
* `MOTION_CSS` in a <style>) anywhere HTML renders; not for <img>.
*/
export function getMotionHTML(wordmark = 'Hanzo') {
return (`<span class="hanzo-motion">` +
`<span class="hanzo-mark">${getMenuBarSVG()}</span>` +
`<span class="hanzo-word"><span>${wordmark}</span></span>` +
`</span>`);
}
/** The shell + its CSS as one self-contained HTML fragment. */
export function getMotionMarkup(wordmark = 'Hanzo') {
return `<style>${MOTION_CSS}</style>${getMotionHTML(wordmark)}`;
}
/**
* The loading state — the origami fold, made indeterminate.
*
* The package had brand motion and no way to say "still working": ANIMATED_SVG
* folds in ONCE and stops (`forwards`), and MOTION_CSS is an intro flip plus a
* breathe loop. Neither is a spinner, so surfaces invented their own — the only
* one in the fleet is ten hand-drawn raster frames.
*
* This is the same fold the mark already does, staggered across the five blocks
* and looping: each block turns in on its edge, holds while the others arrive,
* then turns out. It reads as motion rather than progress, which is what an
* indeterminate state should say.
*
* `currentColor`, so it is visible on any ground — the one-shot animation
* hardcodes `fill:#fff` and is invisible on the white the house asks for.
*/
export const LOADING_CSS = `.hanzo-loading{display:inline-block;line-height:0;color:inherit;width:1.25em;height:1.25em}
.hanzo-loading svg{display:block;width:100%;height:100%}
.hanzo-loading .hz-fold{fill:currentColor;transform-box:fill-box;transform-origin:left center;animation:hanzo-loading-fold 1.8s cubic-bezier(.16,1,.3,1) infinite}
.hanzo-loading .hz-fold-1{animation-delay:.09s}
.hanzo-loading .hz-fold-2{animation-delay:.18s}
.hanzo-loading .hz-fold-3{animation-delay:.27s}
.hanzo-loading .hz-fold-4{animation-delay:.36s}
@keyframes hanzo-loading-fold{0%{opacity:0;transform:perspective(320px) rotateY(90deg)}20%,60%{opacity:1;transform:none}86%,100%{opacity:0;transform:perspective(320px) rotateY(-90deg)}}
@media (prefers-reduced-motion:reduce){.hanzo-loading .hz-fold{animation:none;opacity:1;transform:none}}`;
/**
* The looping mark on its own. Pair with LOADING_CSS, or use
* getLoadingMarkup() for both at once.
*/
export function getLoadingSVG() {
const shadeAt = { 0: MARK_SHADE[0], 3: MARK_SHADE[1] };
const parts = MARK_BLOCKS.map((d, i) => {
const block = `<path class="hz-fold hz-fold-${i}" d="${d}"/>`;
const sliver = shadeAt[i]
? `<path class="hz-fold hz-fold-${i}" opacity=".55" d="${shadeAt[i]}"/>`
: '';
return block + sliver;
});
return (`<svg role="img" aria-label="Loading" viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">` +
parts.join('') +
`</svg>`);
}
/** The loading mark and its CSS as one self-contained fragment. */
export function getLoadingMarkup() {
return `<style>${LOADING_CSS}</style><span class="hanzo-loading">${getLoadingSVG()}</span>`;
}