Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70382d4f10 | ||
|
|
be8468add4 | ||
|
|
915403d9fa | ||
|
|
84586e4ee9 | ||
|
|
5dd8fb3ddd | ||
|
|
4aba18574d | ||
|
|
33b5ef2b2e | ||
|
|
8cf57626fc | ||
|
|
d106e4395a | ||
|
|
5520e7208c | ||
|
|
2aa71b0610 | ||
|
|
1a38d4e6e9 | ||
|
|
33d2945d8e | ||
|
|
1972f6f7bb | ||
|
|
24def2ea2a | ||
|
|
9243dc2f96 | ||
|
|
be91e9d5af | ||
|
|
13f3ac48ca | ||
|
|
ed8c4680d9 | ||
|
|
4a73203aaf | ||
|
|
b5ae14a3fb | ||
|
|
a5aee54c05 | ||
|
|
4fe5b7af54 | ||
|
|
3c86024fe8 | ||
|
|
87dfe5f99c | ||
|
|
b997fd75d7 | ||
|
|
e1340f33a5 | ||
|
|
e766ad4029 | ||
|
|
c10ca626a0 | ||
|
|
7411857e2b | ||
|
|
4bb3ad8124 | ||
|
|
0c69ab4e2d | ||
|
|
f810c4c5cc | ||
|
|
45024af61e | ||
|
|
46a6b6ee67 | ||
|
|
6bee863809 | ||
|
|
ff0071a661 | ||
|
|
e103ddd645 | ||
|
|
eb0d7739c8 | ||
|
|
06041d8e19 | ||
|
|
96a6a32200 | ||
|
|
a15de7064d | ||
|
|
bd804cf291 | ||
|
|
1a3c9d3958 | ||
|
|
f542de6cf1 | ||
|
|
f47bdadb6d | ||
|
|
c3de9bd7d8 | ||
|
|
00eaf1c326 | ||
|
|
f9c300af63 |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" role="img" aria-label="Hanzo brand assets">
|
||||
<rect width="1280" height="640" fill="#0A0A0A"/>
|
||||
<svg x="120" y="200" width="240" height="240" viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
<text x="440" y="316" font-family="Inter,system-ui,-apple-system,sans-serif" font-size="94" font-weight="800" letter-spacing="-3" fill="#ffffff">Hanzo</text>
|
||||
<text x="443" y="366" font-family="Inter,system-ui,sans-serif" font-size="27" fill="#ffffff" opacity=".62">Official brand marks — SVG, PNG, ICO, favicons & app icons.</text>
|
||||
<rect x="443" y="390" width="710" height="2" rx="1" fill="#ffffff" opacity=".85"/>
|
||||
<text x="443" y="440" font-family="Inter,system-ui,sans-serif" font-size="23" font-weight="600" fill="#ffffff" opacity=".45">github.com/hanzoai</text>
|
||||
<text x="1160" y="440" text-anchor="end" font-family="Inter,system-ui,sans-serif" font-size="23" font-weight="600" fill="#ffffff" opacity=".45">hanzo.ai</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -6,6 +6,8 @@ node_modules/
|
||||
# Build output
|
||||
dist/
|
||||
build/
|
||||
# The site directory CI assembles (index.html + hz.js out of node_modules).
|
||||
out/
|
||||
*.tsbuildinfo
|
||||
generated/
|
||||
|
||||
@@ -57,9 +59,6 @@ tmp/
|
||||
|
||||
# But keep important files for GitHub display
|
||||
!menu-bar-preview.html
|
||||
!dist/zoo-logo.svg
|
||||
!dist/zoo-logo-mono.svg
|
||||
!dist/zoo-logo-menubar.svg
|
||||
|
||||
# Keep showcase images for README
|
||||
!docs/assets/*.png
|
||||
@@ -71,3 +70,26 @@ CLAUDE.md
|
||||
GEMINI.md
|
||||
GROK.md
|
||||
QWEN.md
|
||||
|
||||
# ── Logo assets ──────────────────────────────────────────────────────────
|
||||
# Track the RENDERED image assets (png/ico/svg) in dist/ so every size is
|
||||
# browsable in git for easy reference. The npm package ships the full dist
|
||||
# (images + compiled JS) — CI regenerates it on publish (build runs generate).
|
||||
# Compiled JS/maps stay OUT of git (regenerated, churny).
|
||||
!dist/
|
||||
!dist/**/
|
||||
!dist/**/*.png
|
||||
!dist/**/*.ico
|
||||
!dist/**/*.svg
|
||||
!dist/*.svg
|
||||
dist/**/*.js
|
||||
dist/**/*.d.ts
|
||||
dist/**/*.map
|
||||
dist/*.js
|
||||
dist/*.d.ts
|
||||
.npmrc
|
||||
|
||||
# pnpm only — pnpm-lock.yaml is the tracked lockfile.
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
bun.lockb
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# Canonical caller — every knob lives in /hanzo.yml, none here.
|
||||
# Runs on the git-runner fleet at git.hanzo.ai, the only pool serving these
|
||||
# labels. github.com resolves only .github/workflows and has no runner for
|
||||
# them, so a caller placed there is a gate that cannot be scheduled.
|
||||
name: CI/CD
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
cicd:
|
||||
uses: hanzoai/ci/.hanzo/workflows/build.yml@v1
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,75 @@
|
||||
name: deploy
|
||||
|
||||
# logo.hanzo.ai is a SITE, not an App: a static export with no compute. It ships on the
|
||||
# Sites plane. Not GitHub Pages, not Cloudflare Pages, and not an image — baking
|
||||
# files that never execute into a container so a Go binary can serve them means an
|
||||
# image, a push, a tag pinned in a CR and running pods, all to hand back bytes.
|
||||
#
|
||||
# build -> out
|
||||
# -> sitedeploy
|
||||
#
|
||||
# The publish mechanics are NOT here. They are bin/sitedeploy in hanzoai/ci,
|
||||
# reached through its action, and every static surface in the fleet calls that one
|
||||
# script — ensure-project, enqueue, presigned per-file upload, complete. A
|
||||
# contract transcribed per repo is a contract that drifts, and this file used to
|
||||
# carry its own copy of every call. It also SELF-PROVISIONS: the project is
|
||||
# created idempotently before the enqueue, so committing this file is enough to
|
||||
# stand the site up.
|
||||
#
|
||||
# The one secret is HANZO_DEPLOY_TOKEN, which identifies us rather than granting
|
||||
# storage. It is set ON THE FORGE: this file lives under .hanzo/workflows/, which
|
||||
# only git.hanzo.ai reads, so GitHub's secret store is not in this path at all.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['index.html', 'package.json', 'pnpm-lock.yaml', '.hanzo/workflows/deploy.yml']
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: deploy-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
site:
|
||||
# NOT ubuntu-latest. This file lives in .hanzo/workflows, which GitHub never
|
||||
# reads — git.hanzo.ai is the only thing that can run it, and its fleet
|
||||
# advertises hanzo-build-linux-amd64. An unmatched label is not an error here,
|
||||
# it is silence: the job queues until the 24h timeout and nothing says so.
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
# index.html is the whole page — one self-contained file, inline CSS, no
|
||||
# local subresources — so there is nothing to compile. What IS assembled is
|
||||
# the site directory: the page plus hz.js, the script-tag form of
|
||||
# @hanzo/event. hz.js is COPIED OUT OF node_modules, never vendored and
|
||||
# never loaded from a public CDN: the lockfile pins which version ships and
|
||||
# the page keeps its only script same-origin.
|
||||
- name: assemble the site
|
||||
run: |
|
||||
set -euo pipefail
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
rm -rf out && mkdir -p out
|
||||
cp index.html out/index.html
|
||||
cp node_modules/@hanzo/event/hz.js out/hz.js
|
||||
test -s out/hz.js || { echo "::error::@hanzo/event/hz.js missing from the install"; exit 1; }
|
||||
echo "assembled $(find out -type f | wc -l) files"
|
||||
|
||||
# ONE credential. The 202 carries a prefix-scoped, 30-minute presigned POST
|
||||
# grant, so nothing here holds a bucket key. Do NOT add SITES_S3_* — that is
|
||||
# the standing shared-bucket credential the grant replaced, and it could
|
||||
# overwrite every other org's site.
|
||||
- name: Publish to the Sites plane
|
||||
uses: hanzoai/ci/.github/actions/sitedeploy@v1
|
||||
with:
|
||||
slug: logo
|
||||
dir: out
|
||||
env:
|
||||
HANZO_DEPLOY_TOKEN: ${{ secrets.HANZO_DEPLOY_TOKEN }}
|
||||
@@ -0,0 +1,51 @@
|
||||
name: publish
|
||||
|
||||
# Publishes @hanzo/logo when the version in package.json changes on main.
|
||||
# A version already on the registry is skipped, so re-runs are safe.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['package.json']
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: hanzo-build-linux-amd64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- name: Skip if already published
|
||||
id: check
|
||||
run: |
|
||||
NAME=$(jq -r .name package.json)
|
||||
VER=$(jq -r .version package.json)
|
||||
if curl -fsSL "https://registry.npmjs.org/$NAME/$VER" > /dev/null 2>&1; then
|
||||
echo "$NAME@$VER already on registry; skipping"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
# `test` typechecks and then builds — one command for both. The rendered
|
||||
# images under dist/ are tracked but the compiled dist/*.js and dist/*.d.ts
|
||||
# are gitignored, so publishing without this ships every icon and no entry
|
||||
# point. corepack takes the pnpm version from packageManager.
|
||||
- name: Test
|
||||
if: steps.check.outputs.skip != 'true'
|
||||
run: |
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm test
|
||||
- name: Publish
|
||||
if: steps.check.outputs.skip != 'true'
|
||||
# npm, not pnpm: pnpm publish adds git checks a CI checkout fails.
|
||||
# --no-provenance: a token publish 422s without it.
|
||||
run: npm publish --access public --no-provenance
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -0,0 +1,9 @@
|
||||
# logo.hanzo.ai — the logo page, served by hanzoai/static (a Go binary on
|
||||
# scratch) behind hanzoai/ingress. No nginx, no GitHub Pages, no CF Pages: the
|
||||
# site is an image the operator runs like every other Hanzo surface.
|
||||
#
|
||||
# index.html is the whole site: one self-contained page, inline CSS, no scripts
|
||||
# and no local subresources. There is nothing to build, so there is no build
|
||||
# stage — a compile step here would exist only to look like the others.
|
||||
FROM ghcr.io/hanzoai/static:v0.5.1
|
||||
COPY index.html /public/index.html
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Hanzo AI Inc
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,42 +1,121 @@
|
||||
# AI Assistant Knowledge Base
|
||||
# @hanzo/logo
|
||||
|
||||
**Last Updated**: $(date +%Y-%m-%d)
|
||||
**Project**: $(basename "$REPO_PATH")
|
||||
**Organization**: $(basename "$(dirname "$REPO_PATH")")
|
||||
Official Hanzo logo package. Provides SVG logos as inline strings, React components, a pure-CSS brand motion layer, and a build pipeline for generating favicons, Apple touch icons, dock icons, and menu bar assets. Published as `@hanzo/logo` on npm (bump `version` on `main` → CI publishes).
|
||||
|
||||
## Project Overview
|
||||
## Canon
|
||||
The Hanzo mark is the **7-path shaded H** (67x67 viewBox): five body blocks + two
|
||||
`class="shade"` accent slivers (#DDDDDD). `MARK_PATHS` in `src/logos.ts` is the one
|
||||
source of the geometry — every variant (color/mono/white/menubar/animated/motion)
|
||||
renders it. The flat 5-path H is only for tiny favicons (`getFaviconSVG`).
|
||||
|
||||
This repository is part of the $(basename "$(dirname "$REPO_PATH")") organization.
|
||||
## How this ships
|
||||
|
||||
## Essential Commands
|
||||
One way, and it runs on our own stack:
|
||||
|
||||
### Development
|
||||
```bash
|
||||
# Add common commands here
|
||||
push -> github.com/hanzoai/logo where code lands
|
||||
-> git.hanzo.ai/hanzoai/logo pull mirror; runs the CI
|
||||
.hanzo/workflows/publish.yml publishes @hanzo/logo to npm
|
||||
.hanzo/workflows/deploy.yml ships logo.hanzo.ai (Sites plane)
|
||||
|
||||
**Push to GitHub. The forge is a READ-ONLY pull mirror** — `git push` to it is
|
||||
refused with `Mirror Repository hanzoai/logo is read-only`, so the `native`
|
||||
remote is for reading the mirror's state, never for writing. GitHub holds the
|
||||
code; the forge holds the CI, because `.github/workflows/` is empty and every
|
||||
build, check and publish lives under `.hanzo/workflows/`, which only the forge
|
||||
reads. Both use GitHub Actions syntax, so a workflow moves between them by
|
||||
changing directory and nothing else.
|
||||
|
||||
The consequence worth remembering: a push is not a deploy. Nothing runs until
|
||||
the mirror pulls, and that lag is the forge's, not yours.
|
||||
|
||||
`publish.yml` is the SOLE publisher of `@hanzo/logo`. It fires when `version` in
|
||||
`package.json` changes on `main`, skips a version already on the registry, and
|
||||
runs `pnpm test` first — typecheck, then build. The rendered images under `dist/`
|
||||
are tracked but the compiled `dist/*.js` and `dist/*.d.ts` are gitignored, so
|
||||
publishing without that build ships every icon and no entry point. Needs
|
||||
`NPM_TOKEN` as a forge secret.
|
||||
|
||||
### The site
|
||||
|
||||
`index.html` is the whole page: one self-contained file, inline CSS, no local
|
||||
subresources. It is a SITE, not an App — nothing here executes — so it ships on
|
||||
the Sites plane and there is no image, no CR and no pod:
|
||||
|
||||
assemble out/ -> POST /v1/projects/logo/deploy 202 + an upload grant
|
||||
-> POST each file under the grant bytes skip the API
|
||||
-> POST …/deployments/<id>/complete {status, keys}
|
||||
|
||||
This repo holds NO S3 credential. The grant is confined to this site's prefix and
|
||||
expires in 30 minutes; deletion rides the `keys` manifest, because a write-only
|
||||
grant cannot remove a file. The one secret is `HANZO_DEPLOY_TOKEN`, set ON THE
|
||||
FORGE — GitHub's secret store is not in this path at all.
|
||||
|
||||
No GitHub Pages and no Cloudflare Pages: the repo used to push this page to a
|
||||
`gh-pages` branch, Pages was never configured for it, and `logo.hanzo.ai` has
|
||||
answered 404 from our own ingress ever since.
|
||||
|
||||
`out/` is ASSEMBLED, not built — `index.html` plus `hz.js`, the script-tag form of
|
||||
`@hanzo/event`, copied out of `node_modules` at build time. That is the whole
|
||||
reason `@hanzo/event` is a devDependency here: the lockfile decides which version
|
||||
of the telemetry client ships, and the only script this page loads stays
|
||||
same-origin instead of coming off a public CDN. `hz.js` posts to the same
|
||||
`api.hanzo.ai/v1/event` as every bundled Hanzo surface.
|
||||
|
||||
**Known gap, upstream:** `hz.js` (through 0.3.11) cannot authenticate. It sends
|
||||
no `Authorization` header and no `?ingest_key=` query, while `/v1/event` answers
|
||||
`401 ingest_key_required` to an unattributed batch — so every event this page
|
||||
sends is currently refused. The npm client supports `ingestKey` on both the fetch
|
||||
and the beacon; the script form was never given the same. The fix belongs in
|
||||
`hanzoai/ui` `pkgs/event/hz.js` (read `data-ingest-key`, ride it the way
|
||||
`core.ts` already does), NOT in a bespoke script here — a second telemetry client
|
||||
is exactly what `hz.js` exists to prevent.
|
||||
|
||||
## Stack
|
||||
- TypeScript 5, ESM-only output
|
||||
- React (optional peer dep >= 16.8)
|
||||
- sharp (image generation for favicons/icons)
|
||||
- Exports: `.` (main), `./logos` (raw SVG getters), `./motion` (CSS motion layer), `./react` (React components)
|
||||
|
||||
## Structure
|
||||
```
|
||||
src/
|
||||
index.ts # Re-exports logos, motion, types, react
|
||||
logos.ts # MARK_PATHS + SVG generators: getColorSVG, getMonoSVG, getWhiteSVG,
|
||||
# getMenuBarSVG (currentColor), getFaviconSVG, getAnimatedSVG
|
||||
motion.ts # MOTION_CSS + getMotionHTML/getMotionMarkup — intro flip + idle
|
||||
# breathing + wordmark collapse, pure CSS, reduced-motion-safe.
|
||||
# Framework-free strings for Svelte/Tamagui/plain HTML.
|
||||
animated.ts # ANIMATED_SVG — self-contained interactive mark (fold-in → hover
|
||||
# turn → press squash)
|
||||
types.ts # LogoVariant (color|mono|white|favicon|animated), LogoFormat, LogoOptions
|
||||
react.tsx # HanzoLogo (variant + `animated` motion-shell prop), HanzoFavicon
|
||||
build.ts # CLI build script for generating all icon variants (brand-neutral,
|
||||
# reads package.json `brand`)
|
||||
dist/ # Rendered assets (tracked): svg/, favicon/, apple/, dock/, icons/,
|
||||
# menubar/, slack/, showcase.html
|
||||
```
|
||||
|
||||
## Architecture
|
||||
## Commands
|
||||
```bash
|
||||
pnpm install # install dev deps
|
||||
pnpm typecheck # tsc --noEmit
|
||||
pnpm build # clean + compile + build CLI + generate assets
|
||||
pnpm test # typecheck + build (the release gate)
|
||||
```
|
||||
|
||||
## Key Technologies
|
||||
## Usage
|
||||
```tsx
|
||||
import { HanzoLogo } from '@hanzo/logo/react'
|
||||
<HanzoLogo size={64} />
|
||||
<HanzoLogo animated /> // motion shell: flip + breathe + wordmark
|
||||
<HanzoLogo animated wordmark="Lux" /> // white-label
|
||||
|
||||
## Development Workflow
|
||||
import { getColorSVG, getAnimatedSVG } from '@hanzo/logo/logos'
|
||||
import { MOTION_CSS, getMotionHTML } from '@hanzo/logo/motion' // Svelte/Tamagui
|
||||
```
|
||||
|
||||
## Context for All AI Assistants
|
||||
|
||||
This file (`LLM.md`) is symlinked as:
|
||||
- `.AGENTS.md`
|
||||
- `CLAUDE.md`
|
||||
- `QWEN.md`
|
||||
- `GEMINI.md`
|
||||
|
||||
All files reference the same knowledge base. Updates here propagate to all AI systems.
|
||||
|
||||
## Rules for AI Assistants
|
||||
|
||||
1. **ALWAYS** update LLM.md with significant discoveries
|
||||
2. **NEVER** commit symlinked files (.AGENTS.md, CLAUDE.md, etc.) - they're in .gitignore
|
||||
3. **NEVER** create random summary files - update THIS file
|
||||
|
||||
---
|
||||
|
||||
**Note**: This file serves as the single source of truth for all AI assistants working on this project.
|
||||
## Notes
|
||||
- Variants: color (white fill + shade), mono (black fill + shade), white, menubar
|
||||
(currentColor), favicon (simplified 5-path for 16-64px), animated (interactive)
|
||||
- Release: bump the patch in `package.json`, merge to `main`. There is no tag.
|
||||
- `CLAUDE.md`, `AGENTS.md`, `QWEN.md`, `GEMINI.md` are all symlinks to this file
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
# Zoo Logo Generator Makefile
|
||||
# @zooai/logo - TypeScript logo generator for the Zoo ecosystem
|
||||
|
||||
.PHONY: help install build clean dev test publish generate-icons preview lint format typecheck all
|
||||
|
||||
# Default target
|
||||
all: install build
|
||||
|
||||
# Help target - displays available commands
|
||||
help:
|
||||
@echo "Zoo Logo Generator - Make targets:"
|
||||
@echo ""
|
||||
@echo " make install - Install dependencies"
|
||||
@echo " make build - Build TypeScript files"
|
||||
@echo " make clean - Remove build artifacts and dependencies"
|
||||
@echo " make dev - Run in development mode with watch"
|
||||
@echo " make test - Run tests"
|
||||
@echo " make lint - Run ESLint"
|
||||
@echo " make format - Format code with Prettier"
|
||||
@echo " make typecheck - Run TypeScript type checking"
|
||||
@echo " make generate - Generate all logo variations and icons"
|
||||
@echo " make preview - Open preview of generated logos"
|
||||
@echo " make publish - Publish package to npm"
|
||||
@echo " make all - Install and build (default)"
|
||||
@echo ""
|
||||
|
||||
# Install dependencies
|
||||
install:
|
||||
npm install
|
||||
|
||||
# Build TypeScript files
|
||||
build:
|
||||
npm run build
|
||||
|
||||
# Clean build artifacts and dependencies
|
||||
clean:
|
||||
rm -rf dist node_modules package-lock.json
|
||||
@echo "✓ Cleaned build artifacts and dependencies"
|
||||
|
||||
# Development mode with watch
|
||||
dev:
|
||||
npm run dev
|
||||
|
||||
# Run tests
|
||||
test:
|
||||
npm test
|
||||
|
||||
# Run linter
|
||||
lint:
|
||||
npm run lint
|
||||
|
||||
# Format code
|
||||
format:
|
||||
npm run format
|
||||
|
||||
# Type checking
|
||||
typecheck:
|
||||
npm run typecheck
|
||||
|
||||
# Generate all logo variations and icons
|
||||
generate: build
|
||||
npm run generate
|
||||
|
||||
# Preview generated logos (opens in default browser)
|
||||
preview: generate
|
||||
@if [ -f "menu-bar-preview.html" ]; then \
|
||||
open menu-bar-preview.html; \
|
||||
else \
|
||||
echo "Generating preview..."; \
|
||||
npm run preview; \
|
||||
open menu-bar-preview.html; \
|
||||
fi
|
||||
|
||||
# Publish to npm
|
||||
publish: clean install build test
|
||||
npm publish
|
||||
|
||||
# Watch for changes and rebuild
|
||||
watch:
|
||||
npm run watch
|
||||
|
||||
# Install global CLI
|
||||
install-cli:
|
||||
npm link
|
||||
|
||||
# Uninstall global CLI
|
||||
uninstall-cli:
|
||||
npm unlink
|
||||
|
||||
# Check for updates
|
||||
check-updates:
|
||||
npx npm-check-updates
|
||||
|
||||
# Update dependencies
|
||||
update-deps:
|
||||
npx npm-check-updates -u
|
||||
npm install
|
||||
|
||||
# Generate documentation
|
||||
docs:
|
||||
npx typedoc src/index.ts
|
||||
|
||||
# Docker build (for CI/CD)
|
||||
docker-build:
|
||||
docker build -t zooai/logo .
|
||||
|
||||
# Run in Docker
|
||||
docker-run:
|
||||
docker run --rm -v $(PWD)/dist:/app/dist zooai/logo
|
||||
@@ -1,124 +1,56 @@
|
||||
<div align="center">
|
||||
<img src="docs/assets/logo-macos-dock.png" alt="Hanzo Logo" width="256" height="256">
|
||||
<p align="center">
|
||||
<img src=".github/hero.svg" alt="@hanzo/logo" width="100%" />
|
||||
</p>
|
||||
|
||||
# @hanzo/logo
|
||||
# Hanzo — Brand & Logo Assets
|
||||
|
||||
Official Hanzo logo package providing TypeScript/React components and utilities for consistent branding across the Hanzo ecosystem.
|
||||
Official Hanzo brand marks in every format and size — SVG, PNG, multi-resolution ICO, favicons, app/dock/tray icons, and social cards. Installable via `@hanzo/logo`, browsable in `dist/`.
|
||||
|
||||
[](https://www.npmjs.com/package/@hanzo/logo)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.typescriptlang.org/)
|
||||
</div>
|
||||
## Formats
|
||||
|
||||
## Logo Showcase
|
||||
- `svg/` - Vector formats (preferred)
|
||||
- `png/` - Raster formats (various sizes)
|
||||
- `ico/` - Favicon formats
|
||||
|
||||
<div align="center">
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="5">Hanzo Logo - Multiple Sizes</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-32.png" width="32" height="32" alt="32px"><br>
|
||||
<sub>32×32</sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-64.png" width="64" height="64" alt="64px"><br>
|
||||
<sub>64×64</sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-128.png" width="128" height="128" alt="128px"><br>
|
||||
<sub>128×128</sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-256.png" width="256" height="256" alt="256px"><br>
|
||||
<sub>256×256</sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-512.png" width="512" height="512" alt="512px"><br>
|
||||
<sub>512×512</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
## Variants
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>macOS Dock Icon</th>
|
||||
<th>Monochrome</th>
|
||||
<th>Menu Bar Icon</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-macos-dock.png" width="128" height="128" alt="macOS Dock"><br>
|
||||
<sub>Rounded corners + black background</sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-mono-128.png" width="128" height="128" alt="Monochrome"><br>
|
||||
<sub>For single color displays</sub>
|
||||
</td>
|
||||
<td align="center">
|
||||
<img src="docs/assets/logo-menubar-32.png" width="32" height="32" alt="Menu Bar"><br>
|
||||
<sub>Solid filled for menu bars</sub>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
- Primary logo
|
||||
- Wordmark
|
||||
- Icon/Symbol
|
||||
- Monochrome (light/dark)
|
||||
|
||||
## Features
|
||||
## Usage
|
||||
|
||||
- 🎨 **SVG Logo Generation** - Programmatically generate Hanzo logos in multiple formats
|
||||
- ⚛️ **React Components** - Ready-to-use React components with TypeScript support
|
||||
- 🎯 **Multiple Variants** - Color, monochrome, and menu bar optimized versions
|
||||
- 📦 **Icon Generation** - Generate icons in all required sizes for web and desktop apps
|
||||
- 🖥️ **Platform Icons** - macOS dock icons, menu bar icons, and favicons
|
||||
- 🔧 **TypeScript Native** - Full TypeScript support with proper type definitions
|
||||
See [brand guidelines](../brand) for proper usage.
|
||||
|
||||
## Installation
|
||||
## Download
|
||||
|
||||
```bash
|
||||
npm install @hanzo/logo
|
||||
# or
|
||||
yarn add @hanzo/logo
|
||||
# or
|
||||
pnpm add @hanzo/logo
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### React Component
|
||||
|
||||
```tsx
|
||||
import { HanzoLogo } from '@hanzo/logo/react';
|
||||
|
||||
<HanzoLogo size={128} />
|
||||
<HanzoLogo variant="mono" size={64} />
|
||||
<HanzoLogo variant="white" className="w-16 h-16" />
|
||||
```
|
||||
|
||||
### SVG Generation
|
||||
|
||||
```ts
|
||||
import { getColorSVG, getMonoSVG, getWhiteSVG } from '@hanzo/logo';
|
||||
|
||||
const colorLogo = getColorSVG();
|
||||
const monoLogo = getMonoSVG();
|
||||
const whiteLogo = getWhiteSVG();
|
||||
```
|
||||
|
||||
### Icon Generation
|
||||
|
||||
```ts
|
||||
import { generateIcon, getColorSVGCropped } from '@hanzo/logo';
|
||||
|
||||
// Generate macOS dock icon
|
||||
const svg = getColorSVGCropped();
|
||||
await generateIcon(svg, 'dock-icon.png', 512, true);
|
||||
```
|
||||
All assets available in this repository.
|
||||
|
||||
## License
|
||||
|
||||
MIT © Hanzo AI
|
||||
All rights reserved. Usage requires permission.
|
||||
|
||||
## Support
|
||||
## Animated & interactive
|
||||
|
||||
For issues, questions, or suggestions, visit [GitHub Issues](https://github.com/hanzoai/logo/issues)
|
||||
The mark ships a self-contained, **pure-CSS animated SVG** — a load animation, a
|
||||
hover flourish, and a press squash, with **zero JavaScript**. Inline it into the DOM
|
||||
(not via `<img>`) for the hover/press interactions to fire. Honors `prefers-reduced-motion`.
|
||||
|
||||
```tsx
|
||||
// React — one-line interactive embed
|
||||
import { HanzoLogo } from '@hanzo/logo';
|
||||
<HanzoLogo variant="animated" size={64} />
|
||||
```
|
||||
|
||||
```ts
|
||||
// Vanilla / any framework
|
||||
import { getAnimatedSVG, getAnimatedDataUrl } from '@hanzo/logo';
|
||||
el.innerHTML = getAnimatedSVG(); // interactive (inline in the DOM)
|
||||
img.src = getAnimatedDataUrl(); // load animation only (fine in <img>)
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Raw SVG over CDN — load animation in <img>; inline the file for hover/press -->
|
||||
<img src="https://unpkg.com/@hanzo/logo/svg/hanzo-mark-animated.svg" width="64" alt="" />
|
||||
```
|
||||
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 671 B |
|
After Width: | Height: | Size: 661 B |
|
After Width: | Height: | Size: 882 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 801 B |
|
After Width: | Height: | Size: 6.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="64" height="64" rx="8" fill="#000000"/>
|
||||
<g transform="translate(8, 8) scale(0.716)">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 535 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 229 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 366 B |
|
After Width: | Height: | Size: 464 B |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 648 B |
|
After Width: | Height: | Size: 995 B |
|
After Width: | Height: | Size: 224 B |
|
After Width: | Height: | Size: 311 B |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" role="img" aria-label="Hanzo brand assets">
|
||||
<rect width="1280" height="640" fill="#0A0A0A"/>
|
||||
<svg x="120" y="200" width="240" height="240" viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
<text x="440" y="316" font-family="Inter,system-ui,-apple-system,sans-serif" font-size="94" font-weight="800" letter-spacing="-3" fill="#ffffff">Hanzo</text>
|
||||
<text x="443" y="366" font-family="Inter,system-ui,sans-serif" font-size="27" fill="#ffffff" opacity=".62">Official brand marks — SVG, PNG, ICO, favicons & app icons.</text>
|
||||
<rect x="443" y="390" width="710" height="2" rx="1" fill="#ffffff" opacity=".85"/>
|
||||
<text x="443" y="440" font-family="Inter,system-ui,sans-serif" font-size="23" font-weight="600" fill="#ffffff" opacity=".45">github.com/hanzoai</text>
|
||||
<text x="1160" y="440" text-anchor="end" font-family="Inter,system-ui,sans-serif" font-size="23" font-weight="600" fill="#ffffff" opacity=".45">hanzo.ai</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 243 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 360 B |
|
After Width: | Height: | Size: 574 B |
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="currentColor"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="currentColor"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="currentColor"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="currentColor"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="currentColor"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="currentColor"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 611 B |
@@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#000000"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#222222"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#000000"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#000000"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#000000"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#222222"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 290 B |
|
After Width: | Height: | Size: 296 B |
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 360 B |
|
After Width: | Height: | Size: 412 B |
|
After Width: | Height: | Size: 280 B |
|
After Width: | Height: | Size: 433 B |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,299 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Hanzo Logo Assets Showcase</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-dark: #111;
|
||||
--bg-light: #f5f5f5;
|
||||
--grid-bg: repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 50% / 20px 20px;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
padding: 40px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
h1 { margin-bottom: 10px; font-size: 2rem; }
|
||||
.subtitle { color: #888; margin-bottom: 40px; }
|
||||
h2 {
|
||||
margin: 40px 0 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #333;
|
||||
color: #fff;
|
||||
}
|
||||
.section { margin-bottom: 60px; }
|
||||
.grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.icon-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.icon-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--grid-bg);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
.icon-box.dark {
|
||||
background: var(--bg-dark);
|
||||
}
|
||||
.icon-box.light {
|
||||
background: var(--bg-light);
|
||||
}
|
||||
.icon-box img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.label {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
.og-preview {
|
||||
max-width: 600px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
||||
}
|
||||
.og-preview img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
.menubar-demo {
|
||||
background: linear-gradient(to bottom, #d4d4d4, #c8c8c8);
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.menubar-demo.dark {
|
||||
background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
|
||||
}
|
||||
.dock-demo {
|
||||
background: rgba(255,255,255,0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 8px;
|
||||
border-radius: 20px;
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.dock-demo img {
|
||||
border-radius: 22%;
|
||||
}
|
||||
.timestamp {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎨 Hanzo Logo Assets</h1>
|
||||
<p class="subtitle">Every brand mark, format, and size.</p>
|
||||
|
||||
<section class="section">
|
||||
<h2>📁 SVG Sources</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark" style="width:120px;height:120px;">
|
||||
<img src="logo.svg" alt="Color Logo" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">logo.svg</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light" style="width:120px;height:120px;">
|
||||
<img src="logo-mono.svg" alt="Mono Logo" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">logo-mono.svg</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark" style="width:120px;height:120px;">
|
||||
<img src="logo-white.svg" alt="White Logo" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">logo-white.svg</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box" style="width:120px;height:120px;">
|
||||
<img src="favicon.svg" alt="Favicon" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">favicon.svg</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>🖼️ Standard Icons (PNG)</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-32.png" alt="32px"></div>
|
||||
<span class="label">32px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-64.png" alt="64px"></div>
|
||||
<span class="label">64px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-128.png" alt="128px"></div>
|
||||
<span class="label">128px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-256.png" alt="256px"></div>
|
||||
<span class="label">256px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>⭐ Favicons</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-16.png" alt="16px"></div>
|
||||
<span class="label">16px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-32.png" alt="32px"></div>
|
||||
<span class="label">32px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-48.png" alt="48px"></div>
|
||||
<span class="label">48px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-64.png" alt="64px"></div>
|
||||
<span class="label">64px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-192.png" alt="192px"></div>
|
||||
<span class="label">192px (Android)</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-512.png" alt="512px"></div>
|
||||
<span class="label">512px (PWA)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>🍎 Apple Touch Icons</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-60.png" alt="60px"></div>
|
||||
<span class="label">60px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-120.png" alt="120px"></div>
|
||||
<span class="label">120px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-152.png" alt="152px"></div>
|
||||
<span class="label">152px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-180.png" alt="180px"></div>
|
||||
<span class="label">180px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>🖥️ Dock Icons (macOS)</h2>
|
||||
<div class="dock-demo">
|
||||
<img src="dock/dock-64.png" alt="Dock 64" style="width:48px;height:48px;">
|
||||
<img src="dock/dock-128.png" alt="Dock 128" style="width:64px;height:64px;">
|
||||
</div>
|
||||
<div class="grid" style="margin-top: 20px;">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="dock/dock-128.png" alt="128px"></div>
|
||||
<span class="label">128px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="dock/dock-256.png" alt="256px"></div>
|
||||
<span class="label">256px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="dock/dock-512.png" alt="512px"></div>
|
||||
<span class="label">512px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>📱 Menu Bar Icons</h2>
|
||||
<p style="color:#888;margin-bottom:20px;">Template icons adapt to light/dark menu bar</p>
|
||||
<div style="display:flex;gap:40px;flex-wrap:wrap;">
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Light Menu Bar</p>
|
||||
<div class="menubar-demo">
|
||||
<img src="menubar/menubar-16.png" alt="Menu bar icon" style="width:16px;height:16px;">
|
||||
<span style="font-size:13px;color:#333;">Hanzo</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Dark Menu Bar</p>
|
||||
<div class="menubar-demo dark">
|
||||
<img src="menubar/iconTemplate.png" alt="Menu bar icon" style="width:16px;height:16px;filter:invert(1);">
|
||||
<span style="font-size:13px;color:#fff;">Hanzo</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid" style="margin-top:20px;">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light"><img src="menubar/menubar-16.png" alt="16px"></div>
|
||||
<span class="label">16px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light"><img src="menubar/menubar-22.png" alt="22px"></div>
|
||||
<span class="label">22px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light"><img src="menubar/menubar-16@2x.png" alt="16@2x" style="width:32px;"></div>
|
||||
<span class="label">16@2x (32px)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>📣 Open Graph / Social Images</h2>
|
||||
<div style="display:flex;flex-direction:column;gap:30px;">
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">OG Image (1200×630)</p>
|
||||
<div class="og-preview">
|
||||
<img src="og/og-image.png" alt="OG Image">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Twitter Card (1200×600)</p>
|
||||
<div class="og-preview">
|
||||
<img src="og/twitter-card.png" alt="Twitter Card">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Square (1200×1200)</p>
|
||||
<div class="og-preview" style="max-width:400px;">
|
||||
<img src="og/og-square.png" alt="OG Square">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
@@ -0,0 +1,19 @@
|
||||
# Canonical CI/CD config for hanzoai/logo — the one file both the hanzoai/ci
|
||||
# reusable (.hanzo/workflows/cicd.yml) and platform.hanzo.ai read.
|
||||
#
|
||||
# GATE ONLY: @hanzo/logo is an npm package, and the logo site has its own lanes
|
||||
# already (.hanzo/workflows/publish.yml, deploy.yml). A second image build for
|
||||
# the same Dockerfile on the same push buys nothing.
|
||||
#
|
||||
# `pnpm test` here is `pnpm typecheck && pnpm build` — and the build half is the
|
||||
# real gate, because this package's output is GENERATED: `pnpm generate` runs
|
||||
# the compiled dist/build.js, which bakes the tuner's animation states into the
|
||||
# svg + animated.ts that every consumer imports. A break in the generator is a
|
||||
# break in the artifact, and it only shows when the generator is actually run.
|
||||
test:
|
||||
- name: build
|
||||
run: |
|
||||
set -e
|
||||
corepack enable
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm test
|
||||
@@ -0,0 +1,133 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Hanzo AI Logo</title>
|
||||
<meta name="description" content="Hanzo AI official logos and marks">
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0a0a0a;
|
||||
--fg: #fafafa;
|
||||
--muted: #71717a;
|
||||
--accent: #6366f1;
|
||||
--border: #27272a;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
.container { max-width: 800px; text-align: center; }
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.tagline {
|
||||
font-size: 1.25rem;
|
||||
color: var(--muted);
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
.logo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
.logo-card {
|
||||
background: var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 150px;
|
||||
}
|
||||
.logo-card.light { background: #fafafa; }
|
||||
.logo-placeholder {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.05em;
|
||||
}
|
||||
.formats {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.format {
|
||||
padding: 0.5rem 1rem;
|
||||
background: var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.links { display: flex; gap: 1rem; justify-content: center; }
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
a:hover {
|
||||
background: var(--accent);
|
||||
color: var(--bg);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 2rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Hanzo AI Logo</h1>
|
||||
<p class="tagline">Official logos and marks</p>
|
||||
|
||||
<div class="logo-grid">
|
||||
<div class="logo-card">
|
||||
<span class="logo-placeholder" style="color: var(--fg);">H</span>
|
||||
</div>
|
||||
<div class="logo-card light">
|
||||
<span class="logo-placeholder" style="color: #0a0a0a;">H</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="formats">
|
||||
<span class="format">SVG</span>
|
||||
<span class="format">PNG</span>
|
||||
<span class="format">ICO</span>
|
||||
<span class="format">PDF</span>
|
||||
</div>
|
||||
|
||||
<div class="links">
|
||||
<a href="https://github.com/hanzoai/logo">Download All</a>
|
||||
<a href="https://github.com/hanzoai/brand">Brand Guidelines</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">© 2025 Hanzo AI. All rights reserved.</div>
|
||||
|
||||
<!-- hz.js is the script-tag form of @hanzo/event — the SAME client and the
|
||||
SAME wire as every bundled Hanzo surface, POSTing to api.hanzo.ai/v1/event.
|
||||
This page has no bundler, so it gets the script rather than the module.
|
||||
It is served from /hz.js, not a public CDN: CI copies it out of
|
||||
node_modules at build time, so the lockfile decides which version ships and
|
||||
the only script this page loads is same-origin. -->
|
||||
<script async src="/hz.js" data-product="logo" data-host="https://api.hanzo.ai"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Zoo Logo Menu Bar Preview</title>
|
||||
<title>Hanzo Logo Menu Bar Preview</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
@@ -127,7 +127,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Zoo Logo Menu Bar Preview</h1>
|
||||
<h1>Hanzo Logo Menu Bar Preview</h1>
|
||||
<h2>See how the monochrome logo looks in the Mac menu bar</h2>
|
||||
|
||||
<div class="preview-section">
|
||||
@@ -141,7 +141,7 @@
|
||||
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M3 7h10v2H3z M6 3h4v10H6z"/>
|
||||
</svg>
|
||||
<!-- Our Zoo icon -->
|
||||
<!-- Our Hanzo icon -->
|
||||
<svg id="menuIconDark" class="menu-icon" viewBox="98 102 812 820"></svg>
|
||||
<!-- More system icons -->
|
||||
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
|
||||
@@ -161,7 +161,7 @@
|
||||
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M3 7h10v2H3z M6 3h4v10H6z"/>
|
||||
</svg>
|
||||
<!-- Our Zoo icon -->
|
||||
<!-- Our Hanzo icon -->
|
||||
<svg id="menuIconLight" class="menu-icon" viewBox="98 102 812 820"></svg>
|
||||
<!-- More system icons -->
|
||||
<svg class="menu-icon" viewBox="0 0 16 16" fill="currentColor">
|
||||
@@ -290,7 +290,7 @@
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'zoo-logo-mono.svg';
|
||||
a.download = 'hanzo-logo-mono.svg';
|
||||
a.click();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,634 +0,0 @@
|
||||
{
|
||||
"name": "@hanzo/logo",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@hanzo/logo",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sharp": "^0.34.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/node": "^20.19.17",
|
||||
"@types/react": "^18.3.24",
|
||||
"react": "^18.3.1",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz",
|
||||
"integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
|
||||
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz",
|
||||
"integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz",
|
||||
"integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz",
|
||||
"integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz",
|
||||
"integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz",
|
||||
"integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz",
|
||||
"integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz",
|
||||
"integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz",
|
||||
"integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz",
|
||||
"integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz",
|
||||
"integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz",
|
||||
"integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz",
|
||||
"integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz",
|
||||
"integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz",
|
||||
"integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz",
|
||||
"integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz",
|
||||
"integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz",
|
||||
"integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz",
|
||||
"integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz",
|
||||
"integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz",
|
||||
"integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz",
|
||||
"integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz",
|
||||
"integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/minimatch": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
|
||||
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
|
||||
"integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
|
||||
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz",
|
||||
"integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz",
|
||||
"integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.4",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz",
|
||||
"integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.0",
|
||||
"semver": "^7.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.4",
|
||||
"@img/sharp-darwin-x64": "0.34.4",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.3",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.3",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.3",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.3",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.3",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.3",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.3",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.3",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.3",
|
||||
"@img/sharp-linux-arm": "0.34.4",
|
||||
"@img/sharp-linux-arm64": "0.34.4",
|
||||
"@img/sharp-linux-ppc64": "0.34.4",
|
||||
"@img/sharp-linux-s390x": "0.34.4",
|
||||
"@img/sharp-linux-x64": "0.34.4",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.4",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.4",
|
||||
"@img/sharp-wasm32": "0.34.4",
|
||||
"@img/sharp-win32-arm64": "0.34.4",
|
||||
"@img/sharp-win32-ia32": "0.34.4",
|
||||
"@img/sharp-win32-x64": "0.34.4"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "@hanzo/logo",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.14",
|
||||
"packageManager": "pnpm@11.17.0",
|
||||
"description": "Official Hanzo logo package with React components and utilities",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
@@ -8,7 +9,8 @@
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
"README.md",
|
||||
"svg"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
@@ -16,6 +18,16 @@
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js"
|
||||
},
|
||||
"./logos": {
|
||||
"types": "./dist/logos.d.ts",
|
||||
"import": "./dist/logos.js",
|
||||
"require": "./dist/logos.js"
|
||||
},
|
||||
"./motion": {
|
||||
"types": "./dist/motion.d.ts",
|
||||
"import": "./dist/motion.js",
|
||||
"require": "./dist/motion.js"
|
||||
},
|
||||
"./react": {
|
||||
"types": "./dist/react.d.ts",
|
||||
"import": "./dist/react.js",
|
||||
@@ -23,19 +35,19 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run clean && npm run compile && npm run build:cli",
|
||||
"build": "pnpm clean && pnpm compile && pnpm build:cli && pnpm generate",
|
||||
"build:cli": "tsc src/build.ts --outDir dist --module esnext --target es2020 --moduleResolution node --esModuleInterop --skipLibCheck",
|
||||
"compile": "tsc",
|
||||
"clean": "rm -rf dist",
|
||||
"dev": "tsc --watch",
|
||||
"generate": "node dist/build.js",
|
||||
"preview": "npm run generate && open menu-bar-preview.html",
|
||||
"preview": "pnpm generate && open menu-bar-preview.html",
|
||||
"lint": "eslint src --ext .ts,.tsx",
|
||||
"format": "prettier --write 'src/**/*.{ts,tsx,json}'",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "npm run typecheck && npm run build",
|
||||
"test": "pnpm typecheck && pnpm build",
|
||||
"watch": "tsc --watch",
|
||||
"prepublishOnly": "npm run build"
|
||||
"prepublishOnly": "pnpm build"
|
||||
},
|
||||
"keywords": [
|
||||
"hanzo",
|
||||
@@ -47,14 +59,13 @@
|
||||
],
|
||||
"author": "Hanzo AI",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sharp": "^0.34.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hanzo/event": "^0.3.11",
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/node": "^20.19.17",
|
||||
"@types/react": "^18.3.24",
|
||||
"react": "^18.3.1",
|
||||
"sharp": "^0.34.4",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -71,5 +82,11 @@
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"brand": {
|
||||
"name": "Hanzo",
|
||||
"tagline": "Official brand marks — SVG, PNG, ICO, favicons & app icons.",
|
||||
"github": "hanzoai",
|
||||
"domain": "hanzo.ai"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,421 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
'@hanzo/event':
|
||||
specifier: ^0.3.11
|
||||
version: 0.3.11(react@18.3.1)
|
||||
'@types/minimatch':
|
||||
specifier: ^5.1.2
|
||||
version: 5.1.2
|
||||
'@types/node':
|
||||
specifier: ^20.19.17
|
||||
version: 20.19.43
|
||||
'@types/react':
|
||||
specifier: ^18.3.24
|
||||
version: 18.3.31
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
sharp:
|
||||
specifier: ^0.34.4
|
||||
version: 0.34.5
|
||||
typescript:
|
||||
specifier: ^5.9.2
|
||||
version: 5.9.3
|
||||
|
||||
packages:
|
||||
|
||||
'@emnapi/runtime@1.11.3':
|
||||
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
|
||||
|
||||
'@hanzo/event@0.3.11':
|
||||
resolution: {integrity: sha512-HLdDWZPPonel3HPgmDOQ+LqXPV8hPEMZFGnXlFT2gPQq+MxcHgAY482Y9jHCCKwLFo8v805FZ81ylTlBAl5RVQ==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
|
||||
'@img/colour@1.1.0':
|
||||
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||
resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||
resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-ppc64@0.34.5':
|
||||
resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-riscv64@0.34.5':
|
||||
resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-s390x@0.34.5':
|
||||
resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-wasm32@0.34.5':
|
||||
resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@img/sharp-win32-arm64@0.34.5':
|
||||
resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-ia32@0.34.5':
|
||||
resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/minimatch@5.1.2':
|
||||
resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
|
||||
|
||||
'@types/node@20.19.43':
|
||||
resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==}
|
||||
|
||||
'@types/prop-types@15.7.15':
|
||||
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
||||
|
||||
'@types/react@18.3.31':
|
||||
resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
|
||||
react@18.3.1:
|
||||
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
semver@7.8.5:
|
||||
resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
sharp@0.34.5:
|
||||
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@emnapi/runtime@1.11.3':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@hanzo/event@0.3.11(react@18.3.1)':
|
||||
optionalDependencies:
|
||||
react: 18.3.1
|
||||
|
||||
'@img/colour@1.1.0': {}
|
||||
|
||||
'@img/sharp-darwin-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-darwin-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-darwin-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-ppc64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-riscv64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.2.4':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-arm@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-ppc64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-ppc64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-riscv64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-s390x@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linux-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.34.5':
|
||||
optionalDependencies:
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||
optional: true
|
||||
|
||||
'@img/sharp-wasm32@0.34.5':
|
||||
dependencies:
|
||||
'@emnapi/runtime': 1.11.3
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-arm64@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-ia32@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@img/sharp-win32-x64@0.34.5':
|
||||
optional: true
|
||||
|
||||
'@types/minimatch@5.1.2': {}
|
||||
|
||||
'@types/node@20.19.43':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/prop-types@15.7.15': {}
|
||||
|
||||
'@types/react@18.3.31':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.15
|
||||
csstype: 3.2.3
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
dependencies:
|
||||
js-tokens: 4.0.0
|
||||
|
||||
react@18.3.1:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
|
||||
semver@7.8.5: {}
|
||||
|
||||
sharp@0.34.5:
|
||||
dependencies:
|
||||
'@img/colour': 1.1.0
|
||||
detect-libc: 2.1.2
|
||||
semver: 7.8.5
|
||||
optionalDependencies:
|
||||
'@img/sharp-darwin-arm64': 0.34.5
|
||||
'@img/sharp-darwin-x64': 0.34.5
|
||||
'@img/sharp-libvips-darwin-arm64': 1.2.4
|
||||
'@img/sharp-libvips-darwin-x64': 1.2.4
|
||||
'@img/sharp-libvips-linux-arm': 1.2.4
|
||||
'@img/sharp-libvips-linux-arm64': 1.2.4
|
||||
'@img/sharp-libvips-linux-ppc64': 1.2.4
|
||||
'@img/sharp-libvips-linux-riscv64': 1.2.4
|
||||
'@img/sharp-libvips-linux-s390x': 1.2.4
|
||||
'@img/sharp-libvips-linux-x64': 1.2.4
|
||||
'@img/sharp-libvips-linuxmusl-arm64': 1.2.4
|
||||
'@img/sharp-libvips-linuxmusl-x64': 1.2.4
|
||||
'@img/sharp-linux-arm': 0.34.5
|
||||
'@img/sharp-linux-arm64': 0.34.5
|
||||
'@img/sharp-linux-ppc64': 0.34.5
|
||||
'@img/sharp-linux-riscv64': 0.34.5
|
||||
'@img/sharp-linux-s390x': 0.34.5
|
||||
'@img/sharp-linux-x64': 0.34.5
|
||||
'@img/sharp-linuxmusl-arm64': 0.34.5
|
||||
'@img/sharp-linuxmusl-x64': 0.34.5
|
||||
'@img/sharp-wasm32': 0.34.5
|
||||
'@img/sharp-win32-arm64': 0.34.5
|
||||
'@img/sharp-win32-ia32': 0.34.5
|
||||
'@img/sharp-win32-x64': 0.34.5
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Install scripts run only for packages listed here.
|
||||
allowBuilds:
|
||||
sharp: true
|
||||
|
||||
# Our own scopes skip pnpm's new-release quarantine. Scope glob, not a version
|
||||
# pin: pnpm rewrites version entries on every bump and breaks --frozen-lockfile.
|
||||
minimumReleaseAgeExclude:
|
||||
- '@hanzo/*'
|
||||
- '@hanzogui/*'
|
||||
@@ -0,0 +1,5 @@
|
||||
// GENERATED from the @hanzo logo tuner bake, upgraded to the canonical
|
||||
// 7-path shaded H (5 body blocks + 2 shade slivers). Source of truth for the
|
||||
// interaction layer: the tuner's GEN.hanzo() + the shared interaction layer.
|
||||
// Origami fold-in → hover: one 3-D turn → press: squash. Pure CSS, no JS.
|
||||
export const ANIMATED_SVG = "<svg role=\"img\" aria-label=\"Hanzo\" viewBox=\"0 0 67 67\" xmlns=\"http://www.w3.org/2000/svg\">\n<!-- Origami fold-in → hover: one 3-D turn → press: squash. Pure CSS, no JS. Canonical 7-path shaded H. -->\n\n<style>.o{fill:#fff;opacity:0;transform-box:fill-box;transform-origin:left center;animation:fold 0.42s cubic-bezier(.16,1,.3,1) forwards}\n.s{fill:#ddd}\n@keyframes fold{from{opacity:0;transform:perspective(320px) rotateY(90deg)}to{opacity:1;transform:none}}\n.o0{animation-delay:0.00s}.o1{animation-delay:0.06s}.o2{animation-delay:0.12s}.o3{animation-delay:0.18s}.o4{animation-delay:0.24s}\n/* interaction — pure CSS, no JS: hover = one graceful turn (returns to rest), press = squash */\n.hanzo-hov{transform-box:view-box;transform-origin:33.5px 33.5px}\n.hanzo-prs{transform-box:view-box;transform-origin:33.5px 33.5px;transition:transform .22s cubic-bezier(.22,1,.36,1)}\nsvg:hover .hanzo-hov{animation:hanzoTurn 0.85s cubic-bezier(.45,0,.2,1)}\nsvg:active .hanzo-prs{transform:scale(.92)}\n@keyframes hanzoTurn{0%{transform:perspective(300px) rotateY(0)}100%{transform:perspective(300px) rotateY(360deg)}}\n@media (prefers-reduced-motion:reduce){.hanzo-hov,.hanzo-prs{animation:none!important;transform:none!important}.o{opacity:1;animation:none;transform:none}}\n</style>\n<g class=\"hanzo-hov\"><g class=\"hanzo-prs\">\n<path class=\"o o0\" d=\"M22.21 0H0V22.3184H22.21V0Z\"/>\n<path class=\"o o1\" d=\"M66.7198 0H44.5098V22.3184H66.7198V0Z\"/>\n<path class=\"o o1 s\" d=\"M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z\"/>\n<path class=\"o o2\" d=\"M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z\"/>\n<path class=\"o o3\" d=\"M22.21 67V44.6369H0V67H22.21Z\"/>\n<path class=\"o o3 s\" d=\"M0 44.6369L22.21 46.8285V44.6369H0Z\"/>\n<path class=\"o o4\" d=\"M66.7198 67V44.6369H44.5098V67H66.7198Z\"/>\n</g></g>\n</svg>";
|
||||
@@ -1,45 +1,94 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Hanzo Logo Build Script
|
||||
* Generates all required icons for Hanzo ecosystem
|
||||
* Logo Build Script
|
||||
* Generates every brand icon, favicon, and social asset from the canonical
|
||||
* mark. Brand identity (name, tagline, github, domain) is read from
|
||||
* package.json "brand" — this file names no brand, so it is byte-identical
|
||||
* across every logo package.
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import sharp from 'sharp';
|
||||
import { getColorSVG, getMonoSVG, getMenuBarSVG } from './logos.js';
|
||||
import { getColorSVG, getMonoSVG, getMenuBarSVG, getFaviconSVG, getWhiteSVG } from './logos.js';
|
||||
// Namespace view of the same module — lets the build pick up OPTIONAL marks
|
||||
// (e.g. a wordmark) that only some brands define, without a hard import.
|
||||
import * as marks from './logos.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
async function generateIcon(svgString: string, outputPath: string, size: number, addBackground = false): Promise<void> {
|
||||
interface IconConfig {
|
||||
name: string;
|
||||
size: number;
|
||||
svg?: string;
|
||||
addBackground?: boolean;
|
||||
bgColor?: string;
|
||||
cornerRadius?: number;
|
||||
aspectRatio?: { width: number; height: number };
|
||||
}
|
||||
|
||||
async function generateIcon(
|
||||
svgString: string,
|
||||
outputPath: string,
|
||||
size: number,
|
||||
options: {
|
||||
addBackground?: boolean;
|
||||
bgColor?: string;
|
||||
cornerRadius?: number;
|
||||
aspectRatio?: { width: number; height: number };
|
||||
} = {}
|
||||
): Promise<void> {
|
||||
const dir = path.dirname(outputPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
if (addBackground) {
|
||||
// For dock icons, add rounded square black background with more padding
|
||||
const logoSize = Math.floor(size * 0.65); // Reduced from 0.8 for more space
|
||||
const padding = Math.floor((size - logoSize) / 2);
|
||||
const cornerRadius = Math.floor(size * 0.22); // macOS-style corner radius
|
||||
const { addBackground = false, bgColor = 'black', cornerRadius, aspectRatio } = options;
|
||||
|
||||
const bgSvg = `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="${size}" height="${size}" rx="${cornerRadius}" ry="${cornerRadius}" fill="black"/>
|
||||
if (aspectRatio) {
|
||||
// For non-square images like OG graph
|
||||
const { width, height } = aspectRatio;
|
||||
const logoSize = Math.min(width, height) * 0.4;
|
||||
const logoX = Math.floor((width - logoSize) / 2);
|
||||
const logoY = Math.floor((height - logoSize) / 2);
|
||||
const radius = cornerRadius ?? 0;
|
||||
|
||||
const bgSvg = `<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="${width}" height="${height}" rx="${radius}" ry="${radius}" fill="${bgColor}"/>
|
||||
</svg>`;
|
||||
|
||||
// Create background layer
|
||||
const bg = await sharp(Buffer.from(bgSvg)).png().toBuffer();
|
||||
const logo = await sharp(Buffer.from(svgString))
|
||||
.resize(Math.floor(logoSize), Math.floor(logoSize))
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
// Create logo layer
|
||||
await sharp(bg)
|
||||
.composite([{
|
||||
input: logo,
|
||||
top: logoY,
|
||||
left: logoX
|
||||
}])
|
||||
.toFile(outputPath);
|
||||
} else if (addBackground) {
|
||||
// For dock icons, add rounded square background with padding
|
||||
const logoSize = Math.floor(size * 0.65);
|
||||
const padding = Math.floor((size - logoSize) / 2);
|
||||
const radius = cornerRadius ?? Math.floor(size * 0.22);
|
||||
|
||||
const bgSvg = `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="${size}" height="${size}" rx="${radius}" ry="${radius}" fill="${bgColor}"/>
|
||||
</svg>`;
|
||||
|
||||
const bg = await sharp(Buffer.from(bgSvg)).png().toBuffer();
|
||||
const logo = await sharp(Buffer.from(svgString))
|
||||
.resize(logoSize, logoSize)
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
// Composite them together
|
||||
await sharp(bg)
|
||||
.composite([{
|
||||
input: logo,
|
||||
@@ -53,63 +102,588 @@ async function generateIcon(svgString: string, outputPath: string, size: number,
|
||||
.png()
|
||||
.toFile(outputPath);
|
||||
}
|
||||
console.log(`✓ ${path.relative(process.cwd(), outputPath)} (${size}×${size})`);
|
||||
console.log(`✓ ${path.relative(process.cwd(), outputPath)} (${options.aspectRatio ? `${options.aspectRatio.width}×${options.aspectRatio.height}` : `${size}×${size}`})`);
|
||||
}
|
||||
|
||||
interface IconConfig {
|
||||
name: string;
|
||||
size: number;
|
||||
svg?: string;
|
||||
// writeIco packs several PNGs into ONE multi-resolution .ico — the format
|
||||
// Windows favicons, taskbar, and .desktop icons want. Modern ICO embeds the PNG
|
||||
// bytes verbatim (no BMP re-encode), so this is a pure container writer over the
|
||||
// PNGs generateIcon already produced: no new dependency, no image re-processing.
|
||||
// Sizes are read from each source PNG so the directory entry is always correct.
|
||||
async function writeIco(pngPaths: string[], outputPath: string): Promise<void> {
|
||||
const images = await Promise.all(
|
||||
pngPaths.map(async (p) => {
|
||||
const data = fs.readFileSync(p);
|
||||
// PNG IHDR width/height are big-endian uint32 at byte offsets 16/20.
|
||||
const w = data.readUInt32BE(16);
|
||||
const h = data.readUInt32BE(20);
|
||||
return { data, w, h };
|
||||
}),
|
||||
);
|
||||
const count = images.length;
|
||||
const header = Buffer.alloc(6);
|
||||
header.writeUInt16LE(0, 0); // reserved
|
||||
header.writeUInt16LE(1, 2); // type 1 = icon
|
||||
header.writeUInt16LE(count, 4);
|
||||
|
||||
const dir = Buffer.alloc(16 * count);
|
||||
let offset = 6 + 16 * count; // image data starts after header + directory
|
||||
images.forEach((img, i) => {
|
||||
const e = 16 * i;
|
||||
dir.writeUInt8(img.w >= 256 ? 0 : img.w, e + 0); // 0 encodes 256
|
||||
dir.writeUInt8(img.h >= 256 ? 0 : img.h, e + 1);
|
||||
dir.writeUInt8(0, e + 2); // palette count (0 = truecolor)
|
||||
dir.writeUInt8(0, e + 3); // reserved
|
||||
dir.writeUInt16LE(1, e + 4); // color planes
|
||||
dir.writeUInt16LE(32, e + 6); // bits per pixel
|
||||
dir.writeUInt32LE(img.data.length, e + 8); // image size
|
||||
dir.writeUInt32LE(offset, e + 12); // image offset
|
||||
offset += img.data.length;
|
||||
});
|
||||
|
||||
const outDir = path.dirname(outputPath);
|
||||
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
||||
fs.writeFileSync(outputPath, Buffer.concat([header, dir, ...images.map((i) => i.data)]));
|
||||
const dims = images.map((i) => `${i.w}`).join('/');
|
||||
console.log(`✓ ${path.relative(process.cwd(), outputPath)} (${dims})`);
|
||||
}
|
||||
|
||||
// Brand identity for the README hero — read from package.json "brand". Falls
|
||||
// back sensibly so a repo without the block still renders (never the old
|
||||
// "logo" placeholder).
|
||||
interface Brand { name: string; tagline: string; github: string; domain: string; }
|
||||
function readBrand(): Brand {
|
||||
let pkg: any = {};
|
||||
try { pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); } catch { /* defaults */ }
|
||||
const b = pkg.brand || {};
|
||||
const scope = (pkg.name || '').replace(/^@/, '').split('/')[0]; // package scope
|
||||
return {
|
||||
name: b.name || (scope ? scope.charAt(0).toUpperCase() + scope.slice(1) : 'Brand'),
|
||||
tagline: b.tagline || 'Official brand marks — SVG, PNG, ICO, favicons & app icons.',
|
||||
github: b.github || scope,
|
||||
domain: b.domain || '',
|
||||
};
|
||||
}
|
||||
|
||||
// Read once at load — every display string below is driven by this, so the
|
||||
// file carries no brand name of its own.
|
||||
const BRAND = readBrand();
|
||||
|
||||
// generateHero composes the README hero card (1280×640): the brand's own white
|
||||
// mark + the real brand NAME (not the "logo" placeholder the template shipped)
|
||||
// + a full tagline + github/domain footer. Written to .github/hero.svg, which
|
||||
// the README embeds. One template, every brand — driven by readBrand().
|
||||
function generateHero(whiteMark: string): void {
|
||||
const brand = readBrand();
|
||||
const esc = (s: string) => s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
// Embed the brand mark as a nested, positioned SVG. Strip any existing
|
||||
// width/height on the mark's root tag first (some brand marks set them,
|
||||
// which collide with the x/y/width/height we inject and corrupt the SVG);
|
||||
// the viewBox is preserved so scaling stays correct.
|
||||
const cleaned = whiteMark.replace(/^<svg\b[^>]*>/, (tag) => tag.replace(/\s(?:width|height)="[^"]*"/g, ''));
|
||||
const mark = cleaned.replace(/^<svg /, '<svg x="120" y="200" width="240" height="240" ');
|
||||
const hero = `<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="640" viewBox="0 0 1280 640" role="img" aria-label="${esc(brand.name)} brand assets">
|
||||
<rect width="1280" height="640" fill="#0A0A0A"/>
|
||||
${mark}
|
||||
<text x="440" y="316" font-family="Inter,system-ui,-apple-system,sans-serif" font-size="94" font-weight="800" letter-spacing="-3" fill="#ffffff">${esc(brand.name)}</text>
|
||||
<text x="443" y="366" font-family="Inter,system-ui,sans-serif" font-size="27" fill="#ffffff" opacity=".62">${esc(brand.tagline)}</text>
|
||||
<rect x="443" y="390" width="710" height="2" rx="1" fill="#ffffff" opacity=".85"/>
|
||||
<text x="443" y="440" font-family="Inter,system-ui,sans-serif" font-size="23" font-weight="600" fill="#ffffff" opacity=".45">github.com/${esc(brand.github)}</text>
|
||||
<text x="1160" y="440" text-anchor="end" font-family="Inter,system-ui,sans-serif" font-size="23" font-weight="600" fill="#ffffff" opacity=".45">${esc(brand.domain)}</text>
|
||||
</svg>
|
||||
`;
|
||||
fs.mkdirSync('.github', { recursive: true });
|
||||
fs.writeFileSync('.github/hero.svg', hero);
|
||||
// Also ship the hero inside dist/ so it resolves on the npm page via a CDN
|
||||
// (jsdelivr) URL even for packages that aren't backed by a public git repo.
|
||||
fs.writeFileSync('dist/hero.svg', hero);
|
||||
console.log(`✓ .github/hero.svg + dist/hero.svg (${brand.name})`);
|
||||
}
|
||||
|
||||
async function buildAll(): Promise<void> {
|
||||
console.log('🎨 Hanzo Logo Builder\n');
|
||||
console.log(`🎨 ${BRAND.name} Logo Builder\n`);
|
||||
|
||||
const colorSVG = getColorSVG();
|
||||
const monoSVG = getMonoSVG();
|
||||
const menuBarSVG = getMenuBarSVG();
|
||||
const faviconSVG = getFaviconSVG();
|
||||
const whiteSVG = getWhiteSVG();
|
||||
|
||||
// Ensure dist directory exists
|
||||
if (!fs.existsSync('dist')) {
|
||||
fs.mkdirSync('dist');
|
||||
// Ensure dist directories exist
|
||||
const dirs = ['dist', 'dist/icons', 'dist/favicon', 'dist/og', 'dist/apple', 'dist/dock', 'dist/menubar'];
|
||||
for (const dir of dirs) {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
// Save SVG sources
|
||||
fs.writeFileSync('dist/hanzo-logo.svg', colorSVG);
|
||||
fs.writeFileSync('dist/hanzo-logo-mono.svg', monoSVG);
|
||||
fs.writeFileSync('dist/hanzo-logo-menubar.svg', menuBarSVG);
|
||||
console.log('✓ Generated SVG sources\n');
|
||||
console.log('📁 SVG Sources:');
|
||||
fs.writeFileSync('dist/logo.svg', colorSVG);
|
||||
fs.writeFileSync('dist/logo-mono.svg', monoSVG);
|
||||
fs.writeFileSync('dist/logo-white.svg', whiteSVG);
|
||||
fs.writeFileSync('dist/logo-menubar.svg', menuBarSVG);
|
||||
fs.writeFileSync('dist/favicon.svg', faviconSVG);
|
||||
console.log('✓ Generated 5 SVG sources\n');
|
||||
|
||||
// Reference icons
|
||||
if (!fs.existsSync('dist/icons')) {
|
||||
fs.mkdirSync('dist/icons', { recursive: true });
|
||||
// Optional wordmark — brands whose logos.ts exports getWordmarkSVG (a wide
|
||||
// logotype, distinct from the square icon mark) ship it as SVG + aspect-
|
||||
// preserving PNGs. Brands without one skip this entirely: one build, still
|
||||
// brand-neutral, no per-brand wiring here.
|
||||
const wordmark = (marks as any).getWordmarkSVG?.() as string | undefined;
|
||||
const wordmarkWhite = (marks as any).getWordmarkWhite?.() as string | undefined;
|
||||
const wordmarkMono = (marks as any).getWordmarkMono?.() as string | undefined;
|
||||
if (wordmark) {
|
||||
fs.writeFileSync('dist/wordmark.svg', wordmark);
|
||||
if (wordmarkWhite) fs.writeFileSync('dist/wordmark-white.svg', wordmarkWhite);
|
||||
if (wordmarkMono) fs.writeFileSync('dist/wordmark-mono.svg', wordmarkMono);
|
||||
fs.mkdirSync('dist/wordmark', { recursive: true });
|
||||
for (const h of [128, 256, 512]) {
|
||||
// height-only resize preserves the wide aspect ratio (no distortion)
|
||||
await sharp(Buffer.from(wordmark)).resize({ height: h }).png().toFile(`dist/wordmark/wordmark-${h}.png`);
|
||||
console.log(`✓ dist/wordmark/wordmark-${h}.png`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Generating reference icons in dist/:');
|
||||
const distIcons: IconConfig[] = [
|
||||
{ name: 'dist/icons/16.png', size: 16 },
|
||||
{ name: 'dist/icons/32.png', size: 32 },
|
||||
{ name: 'dist/icons/64.png', size: 64 },
|
||||
{ name: 'dist/icons/128.png', size: 128 },
|
||||
{ name: 'dist/icons/256.png', size: 256 },
|
||||
{ name: 'dist/icons/512.png', size: 512 },
|
||||
{ name: 'dist/icons/1024.png', size: 1024 },
|
||||
{ name: 'dist/icons/mono-16.png', size: 16, svg: monoSVG },
|
||||
{ name: 'dist/icons/mono-32.png', size: 32, svg: monoSVG },
|
||||
{ name: 'dist/icons/mono-64.png', size: 64, svg: monoSVG },
|
||||
{ name: 'dist/icons/menubar-16.png', size: 16, svg: menuBarSVG },
|
||||
{ name: 'dist/icons/menubar-32.png', size: 32, svg: menuBarSVG },
|
||||
];
|
||||
// README hero card (brand mark + real brand name), written to .github/.
|
||||
generateHero(whiteSVG);
|
||||
|
||||
for (const icon of distIcons) {
|
||||
await generateIcon(icon.svg || colorSVG, icon.name, icon.size);
|
||||
// === STANDARD ICONS ===
|
||||
console.log('📁 Standard Icons (dist/icons/):');
|
||||
const standardSizes = [16, 32, 64, 128, 256, 512, 1024];
|
||||
for (const size of standardSizes) {
|
||||
await generateIcon(colorSVG, `dist/icons/logo-${size}.png`, size);
|
||||
}
|
||||
for (const size of [16, 32, 64, 128]) {
|
||||
await generateIcon(monoSVG, `dist/icons/logo-mono-${size}.png`, size);
|
||||
}
|
||||
|
||||
// Generate macOS dock icon with background
|
||||
await generateIcon(colorSVG, 'dist/icons/dock-512.png', 512, true);
|
||||
// === FAVICONS ===
|
||||
console.log('\n📁 Favicons (dist/favicon/):');
|
||||
const faviconSizes = [16, 32, 48, 64, 96, 128, 192, 256, 512];
|
||||
for (const size of faviconSizes) {
|
||||
await generateIcon(faviconSVG, `dist/favicon/favicon-${size}.png`, size);
|
||||
}
|
||||
// Also generate with black background versions
|
||||
for (const size of [16, 32, 48]) {
|
||||
await generateIcon(whiteSVG, `dist/favicon/favicon-bg-${size}.png`, size, {
|
||||
addBackground: true,
|
||||
bgColor: '#000000',
|
||||
cornerRadius: 0
|
||||
});
|
||||
}
|
||||
|
||||
// === ICO (Windows favicon / taskbar / .desktop) ===
|
||||
// Pack the favicon PNGs into standard multi-resolution .ico files. The
|
||||
// canonical dist/favicon.ico is what a site drops at its web root; the
|
||||
// per-surface copies keep everything one `ls dist` away.
|
||||
console.log('\n📁 ICO files:');
|
||||
const icoSizes = [16, 32, 48, 64, 128, 256];
|
||||
const faviconPngs = icoSizes.map((s) => `dist/favicon/favicon-${s}.png`);
|
||||
await writeIco(faviconPngs, 'dist/favicon.ico');
|
||||
await writeIco(faviconPngs, 'dist/favicon/favicon.ico');
|
||||
// A color-logo .ico (app/exe icon) from the color icons that exist (the
|
||||
// standard set has no 48px, so use its own sizes, not the favicon set's).
|
||||
await writeIco([16, 32, 64, 128, 256].map((s) => `dist/icons/logo-${s}.png`), 'dist/icons/logo.ico');
|
||||
|
||||
// === WEB-STANDARD NAMED ICONS (drop-in for any site manifest) ===
|
||||
// The exact filenames tools/browsers look for: android-chrome-*, the
|
||||
// default apple-touch-icon.png, and the Windows tile. Same art, canonical
|
||||
// names, so a project can copy dist/favicon/* verbatim.
|
||||
console.log('\n📁 Web-standard named icons (dist/favicon/):');
|
||||
await generateIcon(faviconSVG, 'dist/favicon/android-chrome-192x192.png', 192);
|
||||
await generateIcon(faviconSVG, 'dist/favicon/android-chrome-512x512.png', 512);
|
||||
await generateIcon(colorSVG, 'dist/favicon/apple-touch-icon.png', 180, {
|
||||
addBackground: true, bgColor: '#000000', cornerRadius: 40,
|
||||
});
|
||||
await generateIcon(whiteSVG, 'dist/favicon/mstile-150x150.png', 150, {
|
||||
addBackground: true, bgColor: '#000000',
|
||||
});
|
||||
|
||||
// === APPLE TOUCH ICONS ===
|
||||
console.log('\n📁 Apple Touch Icons (dist/apple/):');
|
||||
const appleSizes = [57, 60, 72, 76, 114, 120, 144, 152, 167, 180];
|
||||
for (const size of appleSizes) {
|
||||
await generateIcon(colorSVG, `dist/apple/apple-touch-icon-${size}.png`, size, {
|
||||
addBackground: true,
|
||||
bgColor: '#000000',
|
||||
cornerRadius: Math.floor(size * 0.156) // iOS corner radius
|
||||
});
|
||||
}
|
||||
|
||||
// === OG GRAPH IMAGES ===
|
||||
console.log('\n📁 Open Graph Images (dist/og/):');
|
||||
// Standard OG (1200x630)
|
||||
await generateIcon(whiteSVG, 'dist/og/og-image.png', 1200, {
|
||||
aspectRatio: { width: 1200, height: 630 },
|
||||
bgColor: '#000000'
|
||||
});
|
||||
// Twitter card (1200x600)
|
||||
await generateIcon(whiteSVG, 'dist/og/twitter-card.png', 1200, {
|
||||
aspectRatio: { width: 1200, height: 600 },
|
||||
bgColor: '#000000'
|
||||
});
|
||||
// Square OG (1200x1200)
|
||||
await generateIcon(whiteSVG, 'dist/og/og-square.png', 1200, {
|
||||
aspectRatio: { width: 1200, height: 1200 },
|
||||
bgColor: '#000000'
|
||||
});
|
||||
|
||||
// === DOCK ICONS (macOS) ===
|
||||
console.log('\n📁 Dock Icons (dist/dock/):');
|
||||
const dockSizes = [64, 128, 256, 512, 1024];
|
||||
for (const size of dockSizes) {
|
||||
await generateIcon(whiteSVG, `dist/dock/dock-${size}.png`, size, {
|
||||
addBackground: true,
|
||||
bgColor: '#000000'
|
||||
});
|
||||
}
|
||||
// @2x versions for Retina
|
||||
for (const base of [128, 256, 512]) {
|
||||
await generateIcon(whiteSVG, `dist/dock/dock-${base}@2x.png`, base * 2, {
|
||||
addBackground: true,
|
||||
bgColor: '#000000'
|
||||
});
|
||||
}
|
||||
|
||||
// === MENU BAR ICONS ===
|
||||
console.log('\n📁 Menu Bar Icons (dist/menubar/):');
|
||||
// Black icons for light menu bar
|
||||
await generateIcon(monoSVG, 'dist/menubar/menubar-16.png', 16);
|
||||
await generateIcon(monoSVG, 'dist/menubar/menubar-16@2x.png', 32);
|
||||
await generateIcon(monoSVG, 'dist/menubar/menubar-16@3x.png', 48);
|
||||
await generateIcon(monoSVG, 'dist/menubar/menubar-22.png', 22);
|
||||
await generateIcon(monoSVG, 'dist/menubar/menubar-22@2x.png', 44);
|
||||
|
||||
// Template icons (for macOS dark/light mode adaptation)
|
||||
await generateIcon(menuBarSVG, 'dist/menubar/iconTemplate.png', 16);
|
||||
await generateIcon(menuBarSVG, 'dist/menubar/iconTemplate@2x.png', 32);
|
||||
await generateIcon(menuBarSVG, 'dist/menubar/iconTemplate@3x.png', 48);
|
||||
|
||||
// === SLACK APP ICON ===
|
||||
// Slack wants a SQUARE 2000×2000 PNG (it applies its own rounded-corner
|
||||
// mask, so the source stays square — cornerRadius 0). Mark centered on the
|
||||
// brand-black field. A 512 copy for anywhere a smaller square is needed.
|
||||
console.log('\n📁 Slack App Icon (dist/slack/):');
|
||||
await generateIcon(colorSVG, 'dist/slack/slack-icon-2000.png', 2000, {
|
||||
addBackground: true, bgColor: '#000000', cornerRadius: 0,
|
||||
});
|
||||
await generateIcon(colorSVG, 'dist/slack/slack-icon-512.png', 512, {
|
||||
addBackground: true, bgColor: '#000000', cornerRadius: 0,
|
||||
});
|
||||
|
||||
// === GENERATE SHOWCASE HTML ===
|
||||
console.log('\n📄 Generating showcase...');
|
||||
generateShowcase();
|
||||
|
||||
console.log('\n✅ Build complete!');
|
||||
console.log(' Open dist/showcase.html in browser to verify all assets\n');
|
||||
}
|
||||
|
||||
function generateShowcase(): void {
|
||||
const html = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${BRAND.name} Logo Assets Showcase</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-dark: #111;
|
||||
--bg-light: #f5f5f5;
|
||||
--grid-bg: repeating-conic-gradient(#ccc 0% 25%, #fff 0% 50%) 50% / 20px 20px;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
padding: 40px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
h1 { margin-bottom: 10px; font-size: 2rem; }
|
||||
.subtitle { color: #888; margin-bottom: 40px; }
|
||||
h2 {
|
||||
margin: 40px 0 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #333;
|
||||
color: #fff;
|
||||
}
|
||||
.section { margin-bottom: 60px; }
|
||||
.grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.icon-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.icon-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--grid-bg);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
.icon-box.dark {
|
||||
background: var(--bg-dark);
|
||||
}
|
||||
.icon-box.light {
|
||||
background: var(--bg-light);
|
||||
}
|
||||
.icon-box img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.label {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
.og-preview {
|
||||
max-width: 600px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
||||
}
|
||||
.og-preview img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
.menubar-demo {
|
||||
background: linear-gradient(to bottom, #d4d4d4, #c8c8c8);
|
||||
padding: 4px 12px;
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.menubar-demo.dark {
|
||||
background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
|
||||
}
|
||||
.dock-demo {
|
||||
background: rgba(255,255,255,0.1);
|
||||
backdrop-filter: blur(20px);
|
||||
padding: 8px;
|
||||
border-radius: 20px;
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.dock-demo img {
|
||||
border-radius: 22%;
|
||||
}
|
||||
.timestamp {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🎨 ${BRAND.name} Logo Assets</h1>
|
||||
<p class="subtitle">Every brand mark, format, and size.</p>
|
||||
|
||||
<section class="section">
|
||||
<h2>📁 SVG Sources</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark" style="width:120px;height:120px;">
|
||||
<img src="logo.svg" alt="Color Logo" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">logo.svg</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light" style="width:120px;height:120px;">
|
||||
<img src="logo-mono.svg" alt="Mono Logo" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">logo-mono.svg</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark" style="width:120px;height:120px;">
|
||||
<img src="logo-white.svg" alt="White Logo" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">logo-white.svg</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box" style="width:120px;height:120px;">
|
||||
<img src="favicon.svg" alt="Favicon" style="width:100px;height:100px;">
|
||||
</div>
|
||||
<span class="label">favicon.svg</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>🖼️ Standard Icons (PNG)</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-32.png" alt="32px"></div>
|
||||
<span class="label">32px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-64.png" alt="64px"></div>
|
||||
<span class="label">64px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-128.png" alt="128px"></div>
|
||||
<span class="label">128px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box dark"><img src="icons/logo-256.png" alt="256px"></div>
|
||||
<span class="label">256px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>⭐ Favicons</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-16.png" alt="16px"></div>
|
||||
<span class="label">16px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-32.png" alt="32px"></div>
|
||||
<span class="label">32px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-48.png" alt="48px"></div>
|
||||
<span class="label">48px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-64.png" alt="64px"></div>
|
||||
<span class="label">64px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-192.png" alt="192px"></div>
|
||||
<span class="label">192px (Android)</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="favicon/favicon-512.png" alt="512px"></div>
|
||||
<span class="label">512px (PWA)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>🍎 Apple Touch Icons</h2>
|
||||
<div class="grid">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-60.png" alt="60px"></div>
|
||||
<span class="label">60px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-120.png" alt="120px"></div>
|
||||
<span class="label">120px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-152.png" alt="152px"></div>
|
||||
<span class="label">152px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="apple/apple-touch-icon-180.png" alt="180px"></div>
|
||||
<span class="label">180px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>🖥️ Dock Icons (macOS)</h2>
|
||||
<div class="dock-demo">
|
||||
<img src="dock/dock-64.png" alt="Dock 64" style="width:48px;height:48px;">
|
||||
<img src="dock/dock-128.png" alt="Dock 128" style="width:64px;height:64px;">
|
||||
</div>
|
||||
<div class="grid" style="margin-top: 20px;">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="dock/dock-128.png" alt="128px"></div>
|
||||
<span class="label">128px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="dock/dock-256.png" alt="256px"></div>
|
||||
<span class="label">256px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box"><img src="dock/dock-512.png" alt="512px"></div>
|
||||
<span class="label">512px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>📱 Menu Bar Icons</h2>
|
||||
<p style="color:#888;margin-bottom:20px;">Template icons adapt to light/dark menu bar</p>
|
||||
<div style="display:flex;gap:40px;flex-wrap:wrap;">
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Light Menu Bar</p>
|
||||
<div class="menubar-demo">
|
||||
<img src="menubar/menubar-16.png" alt="Menu bar icon" style="width:16px;height:16px;">
|
||||
<span style="font-size:13px;color:#333;">${BRAND.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Dark Menu Bar</p>
|
||||
<div class="menubar-demo dark">
|
||||
<img src="menubar/iconTemplate.png" alt="Menu bar icon" style="width:16px;height:16px;filter:invert(1);">
|
||||
<span style="font-size:13px;color:#fff;">${BRAND.name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid" style="margin-top:20px;">
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light"><img src="menubar/menubar-16.png" alt="16px"></div>
|
||||
<span class="label">16px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light"><img src="menubar/menubar-22.png" alt="22px"></div>
|
||||
<span class="label">22px</span>
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<div class="icon-box light"><img src="menubar/menubar-16@2x.png" alt="16@2x" style="width:32px;"></div>
|
||||
<span class="label">16@2x (32px)</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<h2>📣 Open Graph / Social Images</h2>
|
||||
<div style="display:flex;flex-direction:column;gap:30px;">
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">OG Image (1200×630)</p>
|
||||
<div class="og-preview">
|
||||
<img src="og/og-image.png" alt="OG Image">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Twitter Card (1200×600)</p>
|
||||
<div class="og-preview">
|
||||
<img src="og/twitter-card.png" alt="Twitter Card">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p style="margin-bottom:10px;font-size:14px;">Square (1200×1200)</p>
|
||||
<div class="og-preview" style="max-width:400px;">
|
||||
<img src="og/og-square.png" alt="OG Square">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
fs.writeFileSync('dist/showcase.html', html);
|
||||
console.log('✓ dist/showcase.html');
|
||||
}
|
||||
|
||||
// Run the build
|
||||
await buildAll().catch(console.error);
|
||||
await buildAll().catch(console.error);
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Generate showcase images for README
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { getColorSVGCropped, getMonoSVG, getMenuBarSVG } from './logos';
|
||||
|
||||
async function generateShowcaseImages() {
|
||||
console.log('🎨 Generating showcase images for README...\n');
|
||||
|
||||
// Ensure docs/assets directory exists
|
||||
const docsDir = path.join(process.cwd(), 'docs', 'assets');
|
||||
if (!fs.existsSync(docsDir)) {
|
||||
fs.mkdirSync(docsDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Generate cropped color logo
|
||||
const colorSVG = getColorSVGCropped();
|
||||
fs.writeFileSync(path.join(docsDir, 'zoo-logo-cropped.svg'), colorSVG);
|
||||
console.log('✓ Generated cropped color SVG');
|
||||
|
||||
// Generate various sizes of the color logo
|
||||
const sizes = [32, 64, 128, 256, 512];
|
||||
for (const size of sizes) {
|
||||
await sharp(Buffer.from(colorSVG))
|
||||
.resize(size, size)
|
||||
.png()
|
||||
.toFile(path.join(docsDir, `logo-${size}.png`));
|
||||
console.log(`✓ Generated ${size}×${size} color logo`);
|
||||
}
|
||||
|
||||
// Generate monochrome version
|
||||
const monoSVG = getMonoSVG();
|
||||
fs.writeFileSync(path.join(docsDir, 'zoo-logo-mono.svg'), monoSVG);
|
||||
await sharp(Buffer.from(monoSVG))
|
||||
.resize(128, 128)
|
||||
.png()
|
||||
.toFile(path.join(docsDir, 'logo-mono-128.png'));
|
||||
console.log('✓ Generated monochrome logo');
|
||||
|
||||
// Generate menu bar icon
|
||||
const menuBarSVG = getMenuBarSVG();
|
||||
fs.writeFileSync(path.join(docsDir, 'zoo-logo-menubar.svg'), menuBarSVG);
|
||||
await sharp(Buffer.from(menuBarSVG))
|
||||
.resize(32, 32)
|
||||
.png()
|
||||
.toFile(path.join(docsDir, 'logo-menubar-32.png'));
|
||||
console.log('✓ Generated menu bar icon');
|
||||
|
||||
// Generate macOS dock icon with black rounded background
|
||||
const dockSize = 512;
|
||||
const logoSize = Math.floor(dockSize * 0.8);
|
||||
const padding = Math.floor((dockSize - logoSize) / 2);
|
||||
const cornerRadius = Math.floor(dockSize * 0.22);
|
||||
|
||||
const bgSvg = `<svg width="${dockSize}" height="${dockSize}" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="${dockSize}" height="${dockSize}" rx="${cornerRadius}" ry="${cornerRadius}" fill="black"/>
|
||||
</svg>`;
|
||||
|
||||
const bg = await sharp(Buffer.from(bgSvg)).png().toBuffer();
|
||||
const logo = await sharp(Buffer.from(colorSVG))
|
||||
.resize(logoSize, logoSize)
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
await sharp(bg)
|
||||
.composite([{
|
||||
input: logo,
|
||||
top: padding,
|
||||
left: padding
|
||||
}])
|
||||
.toFile(path.join(docsDir, 'logo-macos-dock.png'));
|
||||
console.log('✓ Generated macOS dock icon');
|
||||
|
||||
console.log('\n✅ All showcase images generated!');
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
generateShowcaseImages().catch(console.error);
|
||||
}
|
||||
|
||||
export { generateShowcaseImages };
|
||||
@@ -1,65 +0,0 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import { getColorSVG, getMonoSVG } from './logos';
|
||||
|
||||
/**
|
||||
* Generate a PNG icon from SVG
|
||||
*/
|
||||
export async function generateIcon(
|
||||
svgString: string,
|
||||
outputPath: string,
|
||||
size: number
|
||||
): Promise<void> {
|
||||
const dir = path.dirname(outputPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
await sharp(Buffer.from(svgString))
|
||||
.resize(size, size)
|
||||
.png()
|
||||
.toFile(outputPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all standard icons
|
||||
*/
|
||||
export async function generateAllIcons(outputDir: string = 'dist/icons'): Promise<void> {
|
||||
const colorSVG = getColorSVG();
|
||||
const monoSVG = getMonoSVG();
|
||||
|
||||
// Standard sizes
|
||||
const sizes = [16, 32, 64, 128, 256, 512, 1024];
|
||||
|
||||
console.log('Generating icons...');
|
||||
|
||||
for (const size of sizes) {
|
||||
await generateIcon(colorSVG, path.join(outputDir, `zoo-${size}.png`), size);
|
||||
await generateIcon(monoSVG, path.join(outputDir, `zoo-mono-${size}.png`), size);
|
||||
}
|
||||
|
||||
// Generate @2x versions for macOS
|
||||
const macSizes = [
|
||||
{ base: 16, scale: 2 },
|
||||
{ base: 32, scale: 2 },
|
||||
{ base: 128, scale: 2 },
|
||||
{ base: 256, scale: 2 },
|
||||
{ base: 512, scale: 2 }
|
||||
];
|
||||
|
||||
for (const { base, scale } of macSizes) {
|
||||
await generateIcon(
|
||||
colorSVG,
|
||||
path.join(outputDir, `zoo-${base}@${scale}x.png`),
|
||||
base * scale
|
||||
);
|
||||
}
|
||||
|
||||
// Menu bar templates
|
||||
await generateIcon(monoSVG, path.join(outputDir, 'iconTemplate.png'), 16);
|
||||
await generateIcon(monoSVG, path.join(outputDir, 'iconTemplate@2x.png'), 32);
|
||||
await generateIcon(monoSVG, path.join(outputDir, 'iconTemplate@3x.png'), 48);
|
||||
|
||||
console.log('✅ Icons generated successfully!');
|
||||
}
|
||||
@@ -5,4 +5,6 @@
|
||||
*/
|
||||
|
||||
export * from './logos.js';
|
||||
export * from './types.js';
|
||||
export * from './motion.js';
|
||||
export * from './types.js';
|
||||
export * from './react.js';
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { LogoSettings, LogoOptions } from './types.js';
|
||||
import { ANIMATED_SVG } from './animated.js';
|
||||
|
||||
// Hanzo logo settings
|
||||
export const LOGO_SETTINGS: LogoSettings = {
|
||||
@@ -12,6 +13,26 @@ export const LOGO_SETTINGS: LogoSettings = {
|
||||
}
|
||||
};
|
||||
|
||||
/** Mark viewBox — the block-H's native coordinate space. */
|
||||
export const MARK_VIEWBOX = '0 0 67 67';
|
||||
|
||||
/**
|
||||
* Raw block-H path geometry — the single source of the Hanzo mark.
|
||||
* The canonical mark is SEVEN paths: five fill-less body blocks (callers set
|
||||
* their own `fill` — e.g. `@hanzo/brand` card generators, og images) plus the
|
||||
* two `class="shade"` accent slivers that give the H its shaded depth
|
||||
* (#DDDDDD in every shipped variant; restyle via `.shade` if needed).
|
||||
* Use this instead of re-typing the path data anywhere.
|
||||
*/
|
||||
export const MARK_PATHS =
|
||||
'<path d="M22.21 67V44.6369H0V67H22.21Z"/>' +
|
||||
'<path class="shade" fill="#DDDDDD" d="M0 44.6369L22.21 46.8285V44.6369H0Z"/>' +
|
||||
'<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z"/>' +
|
||||
'<path d="M22.21 0H0V22.3184H22.21V0Z"/>' +
|
||||
'<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z"/>' +
|
||||
'<path class="shade" fill="#DDDDDD" d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z"/>' +
|
||||
'<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z"/>';
|
||||
|
||||
/**
|
||||
* Generate Hanzo color SVG logo
|
||||
*/
|
||||
@@ -28,17 +49,17 @@ export function getColorSVG(): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate monochrome SVG logo
|
||||
* Generate monochrome SVG logo (filled, for dark backgrounds)
|
||||
*/
|
||||
export function getMonoSVG(): string {
|
||||
return `<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="none" stroke="black" stroke-width="1"/>
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#000000"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#222222"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#000000"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#000000"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#000000"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#222222"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#000000"/>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
@@ -81,6 +102,25 @@ export function getMenuBarSVG(): string {
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate favicon SVG (simplified white H on black background)
|
||||
* Optimized for small sizes (16px-64px) - no 3D accents
|
||||
*/
|
||||
export function getFaviconSVG(): string {
|
||||
// Simplified version without 3D accents for better scaling at small sizes
|
||||
// Uses the core Hanzo "H" shape with padding for favicon context
|
||||
return `<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="64" height="64" rx="8" fill="#000000"/>
|
||||
<g transform="translate(8, 8) scale(0.716)">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</g>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logo as data URL
|
||||
*/
|
||||
@@ -95,6 +135,9 @@ export function getLogoDataUrl(options: LogoOptions = {}): string {
|
||||
case 'white':
|
||||
svg = getWhiteSVG();
|
||||
break;
|
||||
case 'favicon':
|
||||
svg = getFaviconSVG();
|
||||
break;
|
||||
default:
|
||||
svg = getColorSVG();
|
||||
}
|
||||
@@ -117,6 +160,9 @@ export function getLogoBase64(options: LogoOptions = {}): string {
|
||||
case 'white':
|
||||
svg = getWhiteSVG();
|
||||
break;
|
||||
case 'favicon':
|
||||
svg = getFaviconSVG();
|
||||
break;
|
||||
default:
|
||||
svg = getColorSVG();
|
||||
}
|
||||
@@ -129,6 +175,11 @@ export function getLogoBase64(options: LogoOptions = {}): string {
|
||||
*/
|
||||
export function getLogo(options: LogoOptions = {}): string {
|
||||
const { variant = 'color', format = 'svg' } = options;
|
||||
if (variant === 'animated') {
|
||||
if (format === 'dataUrl') return getAnimatedDataUrl();
|
||||
if (format === 'base64') return btoa(unescape(encodeURIComponent(getAnimatedSVG())));
|
||||
return getAnimatedSVG();
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case 'dataUrl':
|
||||
@@ -141,6 +192,8 @@ export function getLogo(options: LogoOptions = {}): string {
|
||||
return getMonoSVG();
|
||||
case 'white':
|
||||
return getWhiteSVG();
|
||||
case 'favicon':
|
||||
return getFaviconSVG();
|
||||
default:
|
||||
return getColorSVG();
|
||||
}
|
||||
@@ -151,6 +204,24 @@ export function getLogo(options: LogoOptions = {}): string {
|
||||
export const hanzoLogo = getColorSVG();
|
||||
export const hanzoLogoMono = getMonoSVG();
|
||||
export const hanzoLogoWhite = getWhiteSVG();
|
||||
export const hanzoFavicon = getFaviconSVG();
|
||||
export const hanzoLogoDataUrl = getLogoDataUrl();
|
||||
export const hanzoLogoMonoDataUrl = getLogoDataUrl({ variant: 'mono' });
|
||||
export const hanzoLogoWhiteDataUrl = getLogoDataUrl({ variant: 'white' });
|
||||
export const hanzoLogoWhiteDataUrl = getLogoDataUrl({ variant: 'white' });
|
||||
export const hanzoFaviconDataUrl = getLogoDataUrl({ variant: 'favicon' });
|
||||
/**
|
||||
* Interactive animated mark — load (assemble) → hover (one graceful turn,
|
||||
* returns to rest) → press (squash). Pure CSS, no JS. Inline this SVG into
|
||||
* the DOM (not <img>) for the hover/press interactions to fire.
|
||||
*/
|
||||
export function getAnimatedSVG(): string {
|
||||
return ANIMATED_SVG;
|
||||
}
|
||||
|
||||
/** Animated mark as a data: URL (load animation runs even in <img>). */
|
||||
export function getAnimatedDataUrl(): string {
|
||||
return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(ANIMATED_SVG)));
|
||||
}
|
||||
|
||||
export const hanzoLogoAnimated = ANIMATED_SVG;
|
||||
export const hanzoLogoAnimatedDataUrl = getAnimatedDataUrl();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { getMenuBarSVG } 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'): string {
|
||||
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'): string {
|
||||
return `<style>${MOTION_CSS}</style>${getMotionHTML(wordmark)}`;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { getColorSVG, getMonoSVG, getWhiteSVG } from './logos.js';
|
||||
import { getColorSVG, getMonoSVG, getWhiteSVG, getFaviconSVG, getAnimatedSVG, getMenuBarSVG } from './logos.js';
|
||||
import { MOTION_CSS } from './motion.js';
|
||||
import type { LogoVariant } from './types.js';
|
||||
|
||||
export interface HanzoLogoProps {
|
||||
@@ -7,6 +8,14 @@ export interface HanzoLogoProps {
|
||||
size?: number | string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
/**
|
||||
* Render the brand motion shell: intro flip + idle breathing + a wordmark
|
||||
* that slides in, holds, then collapses (returns on hover). Pure CSS,
|
||||
* reduced-motion-safe. The mark inherits `currentColor`.
|
||||
*/
|
||||
animated?: boolean;
|
||||
/** Wordmark text for the animated shell (white-label). Default "Hanzo". */
|
||||
wordmark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,8 +34,28 @@ export const HanzoLogo: React.FC<HanzoLogoProps> = ({
|
||||
variant = 'color',
|
||||
size = 64,
|
||||
className,
|
||||
style
|
||||
style,
|
||||
animated = false,
|
||||
wordmark = 'Hanzo'
|
||||
}) => {
|
||||
if (animated) {
|
||||
return (
|
||||
<span className={className} style={style}>
|
||||
<style dangerouslySetInnerHTML={{ __html: MOTION_CSS }} />
|
||||
<span className="hanzo-motion">
|
||||
<span
|
||||
className="hanzo-mark"
|
||||
style={{ width: size, height: size }}
|
||||
dangerouslySetInnerHTML={{ __html: getMenuBarSVG() }}
|
||||
/>
|
||||
<span className="hanzo-word">
|
||||
<span>{wordmark}</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let svg = '';
|
||||
|
||||
switch (variant) {
|
||||
@@ -36,6 +65,12 @@ export const HanzoLogo: React.FC<HanzoLogoProps> = ({
|
||||
case 'white':
|
||||
svg = getWhiteSVG();
|
||||
break;
|
||||
case 'favicon':
|
||||
svg = getFaviconSVG();
|
||||
break;
|
||||
case 'animated':
|
||||
svg = getAnimatedSVG();
|
||||
break;
|
||||
default:
|
||||
svg = getColorSVG();
|
||||
}
|
||||
@@ -65,7 +100,7 @@ export const HanzoLogo: React.FC<HanzoLogoProps> = ({
|
||||
* ```
|
||||
*/
|
||||
export const HanzoFavicon: React.FC = () => {
|
||||
const svg = getColorSVG();
|
||||
const svg = getFaviconSVG();
|
||||
const dataUrl = `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
||||
|
||||
return (
|
||||
@@ -74,8 +109,4 @@ export const HanzoFavicon: React.FC = () => {
|
||||
<link rel="apple-touch-icon" href={dataUrl} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Export with backwards compatible names
|
||||
export { HanzoLogo as ZooLogo };
|
||||
export { HanzoFavicon as ZooFavicon };
|
||||
};
|
||||
@@ -9,7 +9,7 @@ export interface LogoSettings {
|
||||
};
|
||||
}
|
||||
|
||||
export type LogoVariant = 'color' | 'mono' | 'white';
|
||||
export type LogoVariant = 'color' | 'mono' | 'white' | 'favicon' | 'animated';
|
||||
export type LogoFormat = 'svg' | 'dataUrl' | 'base64';
|
||||
|
||||
export interface LogoOptions {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<svg role="img" aria-label="Hanzo" viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Origami fold-in → hover: one 3-D turn → press: squash. Pure CSS, no JS. Generated from the @hanzo logo tuner — edit there, not here. -->
|
||||
|
||||
<style>.o{fill:#fff;opacity:0;transform-box:fill-box;transform-origin:left center;animation:fold 0.42s cubic-bezier(.16,1,.3,1) forwards}
|
||||
@keyframes fold{from{opacity:0;transform:perspective(320px) rotateY(90deg)}to{opacity:1;transform:none}}
|
||||
.o0{animation-delay:0.00s}.o1{animation-delay:0.06s}.o2{animation-delay:0.12s}.o3{animation-delay:0.18s}.o4{animation-delay:0.24s}
|
||||
/* interaction — pure CSS, no JS: hover = one graceful turn (returns to rest), press = squash */
|
||||
.hanzo-hov{transform-box:view-box;transform-origin:33.5px 33.5px}
|
||||
.hanzo-prs{transform-box:view-box;transform-origin:33.5px 33.5px;transition:transform .22s cubic-bezier(.22,1,.36,1)}
|
||||
svg:hover .hanzo-hov{animation:hanzoTurn 0.85s cubic-bezier(.45,0,.2,1)}
|
||||
svg:active .hanzo-prs{transform:scale(.92)}
|
||||
@keyframes hanzoTurn{0%{transform:perspective(300px) rotateY(0)}100%{transform:perspective(300px) rotateY(360deg)}}
|
||||
@media (prefers-reduced-motion:reduce){.hanzo-hov,.hanzo-prs{animation:none!important;transform:none!important}.o{opacity:1;animation:none;transform:none}}
|
||||
</style>
|
||||
<g class="hanzo-hov"><g class="hanzo-prs">
|
||||
<path class="o o0" d="M22.21 0H0V22.3184H22.21V0Z"/>
|
||||
<path class="o o1" d="M66.7198 0H44.5098V22.3184H66.7198V0Z"/>
|
||||
<path class="o o2" d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z"/>
|
||||
<path class="o o3" d="M22.21 67V44.6369H0V67H22.21Z"/>
|
||||
<path class="o o4" d="M66.7198 67V44.6369H44.5098V67H66.7198Z"/>
|
||||
</g></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -12,7 +12,7 @@
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"types": ["node"]
|
||||
|
||||