Skip to content

Commit 96c04c9

Browse files
author
JiaDe
committed
feat: ClawForge brand — geometric logo + frontend integration
New brand identity: ClawForge on AgentCore - Hexagon shield + forge anvil + spark particles (pure SVG) - Three states: static (sidebar), idle (chat header), working (loading) - CSS animations: spark shimmer, hammer strike, glow pulse Frontend integration: - ClawForgeLogo.tsx: reusable React component with size/animate/variant props - Login page: ClawForge logo with idle animation - Layout sidebar: ClawForge logo replaces lobster emoji - PortalLayout: ClawForge Portal branding - Portal Chat: idle logo in header, working logo for 'Thinking...' - App loading: working animation during auth check - HTML title: 'ClawForge on AgentCore' - README title updated Brand kit in catnip-mascot/: - clawforge-logo.svg: static icon - clawforge-brand.html: full brand preview with sizes, animations, lockups, colors - Original cat mascot preserved (Apache 2.0)
1 parent 0593f78 commit 96c04c9

16 files changed

Lines changed: 910 additions & 17 deletions

enterprise/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OpenClaw Enterprise on AgentCore
1+
# ClawForge on AgentCore
22

33
Turn [OpenClaw](https://github.com/openclaw/openclaw) from a personal AI assistant into an enterprise-grade digital workforce platform — without modifying a single line of OpenClaw source code.
44

enterprise/admin-console/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>OpenClaw Enterprise on AgentCore</title>
6+
<title>ClawForge on AgentCore</title>
77
<link rel="icon" href="/favicon.ico" />
88
</head>
99
<body>

enterprise/admin-console/src/App.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { AuthProvider, useAuth } from './contexts/AuthContext';
33
import Layout from './components/Layout';
44
import PortalLayout from './components/PortalLayout';
55

6+
import ClawForgeLogo from './components/ClawForgeLogo';
7+
68
// Pages
79
import Login from './pages/Login';
810
import Dashboard from './pages/Dashboard';
@@ -37,8 +39,8 @@ function AppRoutes() {
3739
return (
3840
<div className="flex h-screen items-center justify-center bg-dark-bg">
3941
<div className="text-center">
40-
<div className="inline-flex h-12 w-12 items-center justify-center rounded-xl bg-primary text-white text-xl font-bold mb-3 animate-pulse">O</div>
41-
<p className="text-sm text-text-muted">Loading...</p>
42+
<ClawForgeLogo size={48} animate="working" />
43+
<p className="text-sm text-text-muted mt-3">Loading...</p>
4244
</div>
4345
</div>
4446
);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* ClawForge Logo — Hexagon shield + forge anvil + spark particles.
3+
*
4+
* States:
5+
* - static: no animation (sidebar, favicon)
6+
* - idle: spark shimmer (chat, portal header)
7+
* - working: hammer strike + glow (loading, processing)
8+
*/
9+
10+
interface Props {
11+
size?: number;
12+
animate?: 'static' | 'idle' | 'working';
13+
variant?: 'dark' | 'light'; // dark = white strokes on dark bg, light = dark strokes on light bg
14+
className?: string;
15+
}
16+
17+
export default function ClawForgeLogo({ size = 36, animate = 'static', variant = 'dark', className = '' }: Props) {
18+
const stroke = variant === 'dark' ? '#e2e8f0' : '#1a1d27';
19+
const fill = variant === 'dark' ? '#e2e8f0' : '#1a1d27';
20+
const spark = '#f97316';
21+
const fillLight = variant === 'dark' ? '#94a3b8' : '#374151';
22+
23+
return (
24+
<div className={`inline-flex items-center justify-center relative ${animate === 'working' ? 'animate-forge-working' : ''} ${className}`}>
25+
{/* Glow effect */}
26+
{animate !== 'static' && (
27+
<div className={`absolute inset-0 rounded-full pointer-events-none ${
28+
animate === 'working' ? 'animate-forge-glow bg-primary/20' : 'bg-primary/10'
29+
}`} style={{ width: size * 1.3, height: size * 1.3, top: -(size * 0.15), left: -(size * 0.15) }} />
30+
)}
31+
<svg viewBox="0 0 48 48" width={size} height={size} fill="none" className="relative z-10">
32+
{/* Hexagon shield */}
33+
<path d="M24 3 L42 13.5 L42 34.5 L24 45 L6 34.5 L6 13.5 Z"
34+
stroke={stroke} strokeWidth={2} fill="none" opacity={0.85} />
35+
{/* Anvil */}
36+
<path d="M15 30 L33 30 L30 26 L18 26 Z" fill={fill} opacity={0.7} />
37+
{/* Hammer handle */}
38+
<rect x={22.5} y={14} width={3} height={11} rx={1.5} fill={fillLight}
39+
className={animate === 'working' ? 'animate-forge-hammer' : ''} />
40+
{/* Hammer head */}
41+
<rect x={19} y={12} width={10} height={4} rx={2} fill={fill} opacity={0.9}
42+
className={animate === 'working' ? 'animate-forge-hammer' : ''} />
43+
{/* Sparks */}
44+
<g className={animate === 'idle' ? 'animate-forge-sparkle' : animate === 'working' ? 'animate-forge-spark-burst' : ''}>
45+
<circle cx={14} cy={22} r={1.2} fill={spark} opacity={animate === 'static' ? 0.6 : 0.9} />
46+
<circle cx={34} cy={22} r={1.2} fill={spark} opacity={animate === 'static' ? 0.6 : 0.9} />
47+
{size >= 28 && <>
48+
<circle cx={11} cy={18} r={0.8} fill={spark} opacity={animate === 'static' ? 0.3 : 0.6} />
49+
<circle cx={37} cy={18} r={0.8} fill={spark} opacity={animate === 'static' ? 0.3 : 0.6} />
50+
</>}
51+
{size >= 48 && <>
52+
<circle cx={16} cy={17} r={0.6} fill={spark} opacity={0.4} />
53+
<circle cx={32} cy={17} r={0.6} fill={spark} opacity={0.4} />
54+
</>}
55+
</g>
56+
</svg>
57+
</div>
58+
);
59+
}
60+
61+
export function ClawForgeBrand({ size = 'md', animate = 'idle' as Props['animate'], variant = 'dark' as Props['variant'] }) {
62+
const logoSize = size === 'lg' ? 40 : size === 'md' ? 32 : 24;
63+
const titleSize = size === 'lg' ? 'text-xl' : size === 'md' ? 'text-base' : 'text-sm';
64+
const subSize = size === 'lg' ? 'text-xs' : 'text-[10px]';
65+
const textColor = variant === 'dark' ? 'text-text-primary' : 'text-gray-900';
66+
const subColor = variant === 'dark' ? 'text-text-muted' : 'text-gray-500';
67+
68+
return (
69+
<div className="flex items-center gap-2.5">
70+
<ClawForgeLogo size={logoSize} animate={animate} variant={variant} />
71+
<div>
72+
<div className={`${titleSize} font-extrabold ${textColor} tracking-tight`}>ClawForge</div>
73+
<div className={`${subSize} ${subColor}`}>on AgentCore · aws-samples</div>
74+
</div>
75+
</div>
76+
);
77+
}

enterprise/admin-console/src/components/Layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from 'lucide-react';
88
import { useApprovals, useAlertRules } from '../hooks/useApi';
99
import { useAuth } from '../contexts/AuthContext';
10+
import ClawForgeLogo from './ClawForgeLogo';
1011
import clsx from 'clsx';
1112

1213
interface NavItem {
@@ -167,10 +168,10 @@ export default function Layout({ children }: { children: ReactNode }) {
167168
>
168169
{/* Logo */}
169170
<div className="flex h-16 items-center gap-3 border-b border-dark-border px-4">
170-
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-red-500 to-orange-500 text-lg">🦞</div>
171+
<ClawForgeLogo size={32} animate="idle" />
171172
{sidebarOpen && (
172173
<div className="overflow-hidden">
173-
<div className="text-sm font-semibold text-text-primary truncate">OpenClaw Enterprise</div>
174+
<div className="text-sm font-semibold text-text-primary truncate">ClawForge</div>
174175
<div className="text-[10px] text-text-muted truncate">on AgentCore · aws-samples</div>
175176
</div>
176177
)}

enterprise/admin-console/src/components/PortalLayout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ReactNode } from 'react';
22
import { useNavigate, useLocation } from 'react-router-dom';
33
import { MessageSquare, User, BarChart3, Puzzle, FileText, LogOut } from 'lucide-react';
44
import { useAuth } from '../contexts/AuthContext';
5+
import ClawForgeLogo from './ClawForgeLogo';
56
import clsx from 'clsx';
67

78
const NAV = [
@@ -23,9 +24,9 @@ export default function PortalLayout({ children }: { children: ReactNode }) {
2324
<aside className="flex w-64 flex-col border-r border-dark-border bg-dark-sidebar">
2425
{/* Logo */}
2526
<div className="flex h-16 items-center gap-3 border-b border-dark-border px-4">
26-
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-red-500 to-orange-500 text-lg">🦞</div>
27+
<ClawForgeLogo size={32} animate="idle" />
2728
<div>
28-
<div className="text-sm font-semibold text-text-primary">OpenClaw Portal</div>
29+
<div className="text-sm font-semibold text-text-primary">ClawForge Portal</div>
2930
<div className="text-xs text-text-muted">{user?.name || 'Employee'}</div>
3031
</div>
3132
</div>

enterprise/admin-console/src/index.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,32 @@ body {
3636
.apexcharts-tooltip-title { background: #1a1c23 !important; border-bottom: 1px solid #2e3039 !important; }
3737
.apexcharts-xaxistooltip, .apexcharts-yaxistooltip { background: #24262d !important; border: 1px solid #2e3039 !important; color: #e2e8f0 !important; }
3838
.apexcharts-legend-text { color: #94a3b8 !important; }
39+
40+
/* === ClawForge Logo Animations === */
41+
@keyframes forge-sparkle {
42+
0%, 100% { opacity: 0.3; transform: scale(1); }
43+
50% { opacity: 1; transform: scale(1.4); }
44+
}
45+
@keyframes forge-spark-burst {
46+
0% { opacity: 1; transform: scale(1) translateY(0); }
47+
100% { opacity: 0; transform: scale(0.5) translateY(-4px); }
48+
}
49+
@keyframes forge-hammer {
50+
0%, 100% { transform: translateY(0); }
51+
40% { transform: translateY(-3px); }
52+
60% { transform: translateY(1px); }
53+
}
54+
@keyframes forge-glow {
55+
0%, 100% { opacity: 0.15; transform: scale(1); }
56+
50% { opacity: 0.4; transform: scale(1.1); }
57+
}
58+
@keyframes forge-working {
59+
0%, 100% { transform: scale(1); }
60+
50% { transform: scale(1.02); }
61+
}
62+
63+
.animate-forge-sparkle { animation: forge-sparkle 3s ease-in-out infinite; }
64+
.animate-forge-spark-burst { animation: forge-spark-burst 0.6s ease-out infinite; }
65+
.animate-forge-hammer { animation: forge-hammer 0.6s cubic-bezier(.4,0,.2,1) infinite; }
66+
.animate-forge-glow { animation: forge-glow 1.2s ease-in-out infinite; }
67+
.animate-forge-working { animation: forge-working 1.2s ease-in-out infinite; }

enterprise/admin-console/src/pages/Login.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState } from 'react';
22
import { useNavigate } from 'react-router-dom';
33
import { useAuth } from '../contexts/AuthContext';
44
import { Bot, LogIn, AlertCircle } from 'lucide-react';
5+
import ClawForgeLogo from '../components/ClawForgeLogo';
56

67
const DEMO_ACCOUNTS = [
78
{ id: 'emp-z3', name: 'Zhang San', role: 'Admin', dept: 'Engineering', desc: 'Full platform access' },
@@ -43,8 +44,8 @@ export default function Login() {
4344
<div className="w-full max-w-md">
4445
{/* Logo */}
4546
<div className="text-center mb-8">
46-
<div className="inline-flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-red-500 to-orange-500 text-white text-3xl mb-4">🦞</div>
47-
<h1 className="text-2xl font-bold text-text-primary">OpenClaw Enterprise</h1>
47+
<div className="inline-flex mb-4"><ClawForgeLogo size={56} animate="idle" /></div>
48+
<h1 className="text-2xl font-bold text-text-primary">ClawForge</h1>
4849
<p className="text-sm text-text-muted mt-1">on AgentCore · aws-samples</p>
4950
</div>
5051

enterprise/admin-console/src/pages/portal/Chat.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Send, Bot, User, Loader2, Trash2 } from 'lucide-react';
33
import ReactMarkdown from 'react-markdown';
44
import { useAuth } from '../../contexts/AuthContext';
55
import { api } from '../../api/client';
6+
import ClawForgeLogo from '../../components/ClawForgeLogo';
67

78
interface Message {
89
id: number;
@@ -91,11 +92,9 @@ export default function PortalChat() {
9192
{/* Header */}
9293
<div className="flex items-center justify-between border-b border-dark-border px-6 py-3">
9394
<div className="flex items-center gap-3">
94-
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
95-
<Bot size={22} />
96-
</div>
95+
<ClawForgeLogo size={36} animate="idle" />
9796
<div>
98-
<h1 className="text-sm font-semibold text-text-primary">AI Assistant</h1>
97+
<h1 className="text-sm font-semibold text-text-primary">ClawForge Assistant</h1>
9998
<p className="text-xs text-text-muted">{user?.positionName} · {user?.departmentName}</p>
10099
</div>
101100
</div>
@@ -147,11 +146,10 @@ export default function PortalChat() {
147146
))}
148147
{sending && (
149148
<div className="flex gap-3">
150-
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary mt-1">
151-
<Bot size={16} />
149+
<div className="shrink-0 mt-1">
150+
<ClawForgeLogo size={28} animate="working" />
152151
</div>
153152
<div className="rounded-xl bg-dark-card border border-dark-border px-4 py-3 flex items-center gap-2">
154-
<Loader2 size={16} className="animate-spin text-primary" />
155153
<span className="text-xs text-text-muted">Thinking...</span>
156154
</div>
157155
</div>

enterprise/catnip-mascot/README.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Catnip AI — Free Cat Mascot Kit
2+
3+
> Originally designed for PitchShow's AI chat interface.
4+
> Line-art style, pure SVG, MIT/Apache 2.0. Free to use.
5+
6+
---
7+
8+
## Files
9+
10+
| File | Description |
11+
|------|-------------|
12+
| `cat-icon.svg` | Static icon, transparent bg, 256×256 |
13+
| `catnip-preview.html` | Live preview with all states + code snippets |
14+
15+
---
16+
17+
## SVG (copy-paste)
18+
19+
```svg
20+
<svg viewBox="0 0 48 48" width="28" height="28" fill="none"
21+
stroke="#e5e5e5" stroke-width="2.2" stroke-linecap="round">
22+
<path d="M12,20 Q10,8 16,14"/>
23+
<path d="M36,20 Q38,8 32,14"/>
24+
<ellipse cx="24" cy="27" rx="15" ry="13"/>
25+
<circle cx="18" cy="26" r="2.2" fill="#e5e5e5" stroke="none"/>
26+
<circle cx="30" cy="26" r="2.2" fill="#e5e5e5" stroke="none"/>
27+
<path d="M23,30 L24,31.5 L25,30" stroke-width="1.5"/>
28+
<path d="M22,32.5 Q24,34 26,32.5" stroke-width="1.2"/>
29+
<line x1="4" y1="26" x2="12" y2="28" stroke-width="1" opacity=".6"/>
30+
<line x1="5" y1="30" x2="12" y2="30" stroke-width="1" opacity=".6"/>
31+
<line x1="44" y1="26" x2="36" y2="28" stroke-width="1" opacity=".6"/>
32+
<line x1="43" y1="30" x2="36" y2="30" stroke-width="1" opacity=".6"/>
33+
</svg>
34+
```
35+
36+
## Customise stroke color
37+
38+
- White on dark: `stroke="#e5e5e5"` (default)
39+
- Purple AI: `stroke="#a78bfa"`
40+
- Dark on light: `stroke="#1a1a2c"`
41+
- Custom: replace `stroke` value with any CSS color
42+
43+
## CSS Animations
44+
45+
```css
46+
/* Idle: slow blink */
47+
.cat-eyes {
48+
animation: catBlink 5s ease-in-out infinite;
49+
transform-origin: center 26px;
50+
transform-box: fill-box;
51+
}
52+
/* Working: head wobble + fast double blink */
53+
.cat-working { animation: catTilt 1.5s cubic-bezier(.4,0,.2,1) infinite; }
54+
.cat-working .cat-eyes { animation: catBlinkFast 2s cubic-bezier(.4,0,.2,1) infinite; }
55+
56+
@keyframes catBlink {
57+
0%,38%,42%,100% { transform: scaleY(1); }
58+
40% { transform: scaleY(0.05); }
59+
}
60+
@keyframes catBlinkFast {
61+
0%,35%,42%,100% { transform: scaleY(1); }
62+
38% { transform: scaleY(0.05); }
63+
70%,74% { transform: scaleY(1); }
64+
72% { transform: scaleY(0.05); }
65+
}
66+
@keyframes catTilt {
67+
0%,100% { transform: rotate(-5deg); }
68+
50% { transform: rotate(5deg); }
69+
}
70+
```
71+
72+
## React component
73+
74+
```tsx
75+
interface Props { size?: number; animate?: 'idle' | 'working' | 'none' }
76+
function CatIcon({ size = 28, animate = 'idle' }: Props) {
77+
const s = '#e5e5e5';
78+
return (
79+
<svg viewBox="0 0 48 48" width={size} height={size} fill="none"
80+
stroke={s} strokeWidth={2.2} strokeLinecap="round">
81+
<path d="M12,20 Q10,8 16,14"/>
82+
<path d="M36,20 Q38,8 32,14"/>
83+
<ellipse cx="24" cy="27" rx="15" ry="13"/>
84+
<g className={animate === 'idle' ? 'cat-eyes' : animate === 'working' ? 'cat-eyes-fast' : ''}>
85+
<circle cx="18" cy="26" r="2.2" fill={s} stroke="none"/>
86+
<circle cx="30" cy="26" r="2.2" fill={s} stroke="none"/>
87+
</g>
88+
<path d="M23,30 L24,31.5 L25,30" strokeWidth={1.5}/>
89+
<path d="M22,32.5 Q24,34 26,32.5" strokeWidth={1.2}/>
90+
<line x1="4" y1="26" x2="12" y2="28" strokeWidth={1} opacity={0.6}/>
91+
<line x1="5" y1="30" x2="12" y2="30" strokeWidth={1} opacity={0.6}/>
92+
<line x1="44" y1="26" x2="36" y2="28" strokeWidth={1} opacity={0.6}/>
93+
<line x1="43" y1="30" x2="36" y2="30" strokeWidth={1} opacity={0.6}/>
94+
</svg>
95+
);
96+
}
97+
```
98+
99+
---
100+
101+
## License
102+
103+
Apache 2.0 — free to use commercially and personally.
104+
Credit appreciated but not required: *"Catnip AI by Wu_JiaDe / PitchShow"*

0 commit comments

Comments
 (0)