feat: Hermes Workspace branding — Nous blue theme, caduceus avatar, all ClawSuite→Hermes text, Powered by badge

This commit is contained in:
outsourc-e
2026-03-15 00:00:36 -04:00
parent 69fe4d12ab
commit 3f16cf9042
49 changed files with 169 additions and 171 deletions

View File

@@ -1,5 +1,5 @@
/**
* ClawSuite Electron Main Process
* Hermes Workspace Electron Main Process
* Wraps the Vite-built web app in a native desktop window
*/
@@ -104,7 +104,7 @@ function createWindow() {
height: 900,
minWidth: 800,
minHeight: 600,
title: 'ClawSuite',
title: 'Hermes Workspace',
icon: existsSync(iconPath) ? iconPath : undefined,
titleBarStyle: 'hiddenInset', // macOS native title bar
trafficLightPosition: { x: 16, y: 16 },
@@ -155,10 +155,10 @@ function createTray() {
if (!existsSync(iconPath)) return
tray = new Tray(nativeImage.createFromPath(iconPath))
tray.setToolTip('ClawSuite')
tray.setToolTip('Hermes Workspace')
const contextMenu = Menu.buildFromTemplate([
{ label: 'Open ClawSuite', click: () => mainWindow?.show() },
{ label: 'Open Hermes Workspace', click: () => mainWindow?.show() },
{ type: 'separator' },
{ label: 'Gateway Status', enabled: false },
{ type: 'separator' },
@@ -269,4 +269,4 @@ app.on('before-quit', () => {
})
// Set app name
app.setName('ClawSuite')
app.setName('Hermes Workspace')

View File

@@ -1,5 +1,5 @@
/**
* ClawSuite Electron Preload Script
* Hermes Workspace Electron Preload Script
* Exposes safe IPC bridge to renderer
*/

View File

@@ -1,7 +1,7 @@
{
"name": "clawsuite",
"version": "4.0.0",
"description": "Supercharged dashboard for OpenClaw AI agents — chat, file explorer, terminal, and usage tracking",
"name": "hermes-workspace",
"version": "1.0.0-hackathon",
"description": "Desktop workspace for Hermes Agent — chat, orchestration, and multi-agent coding pipelines",
"author": "Eric (https://github.com/outsourc-e)",
"license": "MIT",
"repository": {

BIN
public/hermes-favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
public/hermes-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
public/hermes-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -23,7 +23,7 @@ const TIPS = [
emoji: '🧠',
text: 'Your agent has memory — it remembers context across sessions',
},
{ emoji: '🚀', text: 'ClawSuite works with any OpenClaw gateway instance' },
{ emoji: '🚀', text: 'Hermes Workspace works with any OpenClaw gateway instance' },
{
emoji: '🎯',
text: 'Pin important sessions to keep them at the top of your sidebar',

View File

@@ -478,7 +478,7 @@ export function IsometricOffice({ sessions, className }: IsometricOfficeProps) {
{/* Office info */}
<div className="absolute bottom-3 left-3 rounded bg-slate-900/80 px-2 py-1 backdrop-blur">
<span className="text-[9px] font-mono text-accent-400/60">
🦞 ClawSuite Office
🦞 Hermes Workspace Office
</span>
</div>

View File

@@ -60,7 +60,7 @@ export function LoginScreen() {
<circle cx="50" cy="50" r="15" fill="currentColor" />
</svg>
<h1 className="text-2xl font-bold tracking-tight text-primary-900">
ClawSuite
Hermes Workspace
</h1>
</div>
</div>

View File

@@ -7,8 +7,7 @@ type AvatarProps = {
}
/**
* Assistant avatar — matches the ClawSuite favicon/hero logo.
* Orange gradient rounded square with dark claw brackets < | >
* Assistant avatar — Hermes Agent caduceus on Nous blue.
*/
function AssistantAvatarComponent({ size = 28, className }: AvatarProps) {
return (
@@ -20,41 +19,19 @@ function AssistantAvatarComponent({ size = 28, className }: AvatarProps) {
style={{ width: size, height: size }}
>
<defs>
<linearGradient id="ava-orange" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#ea580c" />
<stop offset="50%" stopColor="#f97316" />
<stop offset="100%" stopColor="#fb923c" />
<linearGradient id="ava-hermes" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#1E30AA" />
<stop offset="50%" stopColor="#3050FF" />
<stop offset="100%" stopColor="#5070FF" />
</linearGradient>
</defs>
{/* Orange background */}
<rect
x="5"
y="5"
width="90"
height="90"
rx="20"
fill="url(#ava-orange)"
/>
{/* Left claw bracket */}
<path
d="M 40 35 L 30 50 L 40 65"
stroke="#1e293b"
strokeWidth="5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
{/* Right claw bracket */}
<path
d="M 60 35 L 70 50 L 60 65"
stroke="#1e293b"
strokeWidth="5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
{/* Center cursor bar */}
<rect x="47" y="40" width="6" height="20" rx="3" fill="#1e293b" />
<rect x="5" y="5" width="90" height="90" rx="20" fill="url(#ava-hermes)" />
<rect x="47" y="22" width="6" height="56" rx="3" fill="#FFD700" />
<path d="M 35 30 Q 50 22, 50 30" stroke="#FFD700" strokeWidth="3" fill="none" strokeLinecap="round" />
<path d="M 65 30 Q 50 22, 50 30" stroke="#FFD700" strokeWidth="3" fill="none" strokeLinecap="round" />
<path d="M 38 58 Q 30 50, 38 42 Q 46 34, 50 38" stroke="#E8ECFF" strokeWidth="3.5" fill="none" strokeLinecap="round" />
<path d="M 62 58 Q 70 50, 62 42 Q 54 34, 50 38" stroke="#E8ECFF" strokeWidth="3.5" fill="none" strokeLinecap="round" />
<circle cx="50" cy="22" r="5" fill="#FFD700" />
</svg>
)
}

View File

@@ -72,8 +72,8 @@ export function GatewayConnectionSetupForm({
>
<div className={cn('flex gap-3', isBanner ? 'items-start' : 'items-start sm:items-center')}>
<img
src="/logo-icon.png"
alt="ClawSuite logo"
src="/hermes-icon.png"
alt="Hermes Workspace logo"
width={isBanner ? 24 : 32}
height={isBanner ? 24 : 32}
className={cn(

View File

@@ -44,7 +44,7 @@ const CLOUD_PLAN_OPTIONS: Array<{
plan: 'free',
name: 'Free',
price: '$0/mo',
description: 'Try ClawSuite Cloud with free AI models',
description: 'Try Hermes Workspace Cloud with free AI models',
cta: 'Start Free',
},
{
@@ -283,7 +283,7 @@ function GatewayStepContent() {
const normalizedEmail = waitlistEmail.trim()
if (!normalizedEmail) {
setCloudProvisionStatus('error')
setCloudProvisionError('Enter your email to provision a ClawSuite Cloud gateway.')
setCloudProvisionError('Enter your email to provision a Hermes Workspace Cloud gateway.')
return
}
@@ -309,7 +309,7 @@ function GatewayStepContent() {
const errorMessage =
data && 'error' in data && typeof data.error === 'string'
? data.error
: 'Failed to provision your free ClawSuite Cloud gateway.'
: 'Failed to provision your free Hermes Workspace Cloud gateway.'
setCloudProvisionStatus('error')
setCloudProvisionError(errorMessage)
return
@@ -329,7 +329,7 @@ function GatewayStepContent() {
setCloudProvisionStatus('success')
} catch {
setCloudProvisionStatus('error')
setCloudProvisionError('Failed to provision your free ClawSuite Cloud gateway.')
setCloudProvisionError('Failed to provision your free Hermes Workspace Cloud gateway.')
}
}
@@ -392,15 +392,15 @@ function GatewayStepContent() {
<div className="mb-6 flex flex-col items-center text-center">
<div className="mb-4 flex size-20 items-center justify-center rounded-2xl shadow-lg">
<img
src="/logo-icon.png"
alt="ClawSuite logo"
src="/hermes-icon.png"
alt="Hermes Workspace logo"
width={64}
height={64}
className="size-16"
/>
</div>
<h2 className="mb-2 text-2xl font-semibold text-primary-900">
Welcome to ClawSuite
Welcome to Hermes Workspace
</h2>
<p className="max-w-md text-sm leading-relaxed text-primary-600">
Your AI command center
@@ -425,7 +425,7 @@ function GatewayStepContent() {
/>
<SetupModeCard
icon={CloudIcon}
title="ClawSuite Cloud"
title="Hermes Workspace Cloud"
description="No setup needed. Managed hosting with one click. (Coming soon)"
selected={setupMode === 'cloud'}
onClick={() => handleSetupModeChange('cloud')}
@@ -438,7 +438,7 @@ function GatewayStepContent() {
<div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
<div>
<h3 className="text-base font-semibold text-primary-900">
ClawSuite Cloud Plans
Hermes Workspace Cloud Plans
</h3>
<p className="mt-1 text-sm text-primary-600">
Use this email as your Cloud login, then start free or continue to
@@ -582,7 +582,7 @@ function GatewayStepContent() {
<div className="mb-4">
<h3 className="text-base font-semibold text-primary-900">Setting up local gateway</h3>
<p className="mt-1 text-sm text-primary-600">
ClawSuite is installing and starting OpenClaw in the background.
Hermes Workspace is installing and starting OpenClaw in the background.
</p>
</div>

View File

@@ -113,7 +113,7 @@ export function MobilePromptTrigger() {
<div className="min-w-0 flex-1 text-center">
<p className="text-sm font-semibold text-white">Set up mobile access</p>
<p className="text-xs text-primary-300">
Connect your phone to this ClawSuite instance in a few steps.
Connect your phone to this Hermes Workspace instance in a few steps.
</p>
</div>

View File

@@ -79,7 +79,7 @@ export function MobileSetupModal({ isOpen, onClose }: MobileSetupModalProps) {
const steps = [
{
title: 'Install Tailscale on your desktop',
body: 'Install Tailscale on the machine running ClawSuite, then sign in.',
body: 'Install Tailscale on the machine running Hermes Workspace, then sign in.',
showTailscaleIcon: true,
action: (
<a
@@ -148,7 +148,7 @@ export function MobileSetupModal({ isOpen, onClose }: MobileSetupModalProps) {
),
},
{
title: 'Open ClawSuite on your phone',
title: 'Open Hermes Workspace on your phone',
body: networkUrl?.source === 'tailscale'
? 'Your Tailscale address — open this on your phone browser.'
: networkUrl?.source === 'lan'

View File

@@ -23,7 +23,7 @@ export type OnboardingStep = {
export const ONBOARDING_STEPS: OnboardingStep[] = [
{
id: 'welcome',
title: 'Welcome to ClawSuite',
title: 'Welcome to Hermes Workspace',
description:
"Your intelligent workspace for AI-powered automation. Let's take a quick tour of what you can do.",
icon: Home01Icon,

View File

@@ -6,7 +6,7 @@ export const tourSteps: Step[] = [
{
target: 'body',
placement: 'center',
title: 'Welcome to ClawSuite! 👋',
title: 'Welcome to Hermes Workspace! 👋',
content: (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '12px' }}>
<OpenClawStudioIcon className="size-12 rounded-xl shadow-sm" />
@@ -63,7 +63,7 @@ export const tourSteps: Step[] = [
placement: 'right',
title: 'Built-in Terminal',
content:
'Built-in terminal for quick commands. Execute shell commands without leaving ClawSuite.',
'Built-in terminal for quick commands. Execute shell commands without leaving Hermes Workspace.',
},
// Step 9: Usage Meter (in header)
{
@@ -79,7 +79,7 @@ export const tourSteps: Step[] = [
placement: 'right',
title: 'Settings & Customization',
content:
'Configure providers, themes, accent colors, and more. Make ClawSuite yours.',
'Configure providers, themes, accent colors, and more. Make Hermes Workspace yours.',
},
// Step 11: Finish
{
@@ -87,6 +87,6 @@ export const tourSteps: Step[] = [
placement: 'center',
title: "You're all set! 🎉",
content:
'Start chatting with your AI, explore the tools, and customize ClawSuite to fit your workflow. Need help? Press ? to see all keyboard shortcuts.',
'Start chatting with your AI, explore the tools, and customize Hermes Workspace to fit your workflow. Need help? Press ? to see all keyboard shortcuts.',
},
]

View File

@@ -63,7 +63,7 @@ const DARK_ENTERPRISE_THEMES = new Set<ThemeId>([
'ops-dark',
'premium-dark',
'sunset-brand',
])
'hermes',])
function isDarkEnterpriseTheme(theme: string | null): theme is ThemeId {
if (!theme) return false
@@ -427,6 +427,13 @@ const ENTERPRISE_THEMES = [
desc: 'Warm brown brand immersion',
preview: { bg: '#1a0e05', panel: '#2a1a0e', border: '#6b3c1b', accent: '#f59e0b', text: '#ffe7d1' },
},
{
id: 'hermes',
label: 'Hermes',
icon: '⚕',
desc: 'Nous Research deep blue',
preview: { bg: '#0A0E1A', panel: '#1A2240', border: 'rgba(48,80,255,0.18)', accent: '#3050FF', text: '#E8ECFF' },
},
] as const
function ThemeSwatch({ colors }: { colors: typeof ENTERPRISE_THEMES[number]['preview'] }) {
@@ -839,7 +846,7 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
Settings
</DialogTitle>
<DialogDescription className="sr-only">
Configure ClawSuite
Configure Hermes Workspace
</DialogDescription>
</div>
<DialogClose

View File

@@ -260,7 +260,7 @@ export function UpdateNotifier() {
<div className="flex-1 min-w-0">
<p className="text-sm font-semibold">
{phase === 'idle' || phase === 'error'
? 'ClawSuite Update'
? 'Hermes Workspace Update'
: PHASE_LABELS[phase]}
</p>
<p className="text-xs text-primary-400 truncate">

View File

@@ -114,7 +114,7 @@ export function WorkspaceShell() {
}
throw error instanceof Error
? error
: new Error('Failed to connect to ClawSuite server')
: new Error('Failed to connect to Hermes Workspace server')
} finally {
globalThis.clearTimeout(timeout)
}
@@ -247,7 +247,7 @@ export function WorkspaceShell() {
<div className="flex items-center justify-center h-screen bg-surface">
<div className="text-center">
<div className="inline-block h-10 w-10 animate-spin rounded-full border-4 border-accent-500 border-r-transparent mb-4" />
<p className="text-sm text-primary-500">Initializing ClawSuite...</p>
<p className="text-sm text-primary-500">Initializing Hermes Workspace...</p>
</div>
</div>
)
@@ -257,7 +257,7 @@ export function WorkspaceShell() {
const errorMessage =
authQuery.error instanceof Error
? authQuery.error.message
: 'Failed to connect to ClawSuite server'
: 'Failed to connect to Hermes Workspace server'
const showGatewayTip = /gateway|websocket/i.test(errorMessage)
return (
@@ -269,7 +269,7 @@ export function WorkspaceShell() {
</span>
</div>
<h1 className="text-2xl font-semibold text-primary-100">
Could not connect to ClawSuite server
Could not connect to Hermes Workspace server
</h1>
<p className="mt-3 text-sm text-primary-300">
The server may still be starting up. Wait a moment and try again.
@@ -335,7 +335,7 @@ export function WorkspaceShell() {
<div className="w-[78px] shrink-0" />
{/* Centered title */}
<div className="flex-1 text-center">
<span className="text-[13px] font-medium text-primary-600 dark:text-primary-400 select-none">ClawSuite</span>
<span className="text-[13px] font-medium select-none" style={{ color: "var(--theme-accent, #3050FF)" }}> Hermes Workspace</span>
</div>
{/* Right spacer to balance */}
<div className="w-[78px] shrink-0" />

View File

@@ -56,7 +56,7 @@ export async function pingGateway(): Promise<{ ok: boolean; error?: string }> {
const data = (await response.json()) as { ok?: boolean; error?: string }
return { ok: Boolean(data.ok), error: data.error }
} catch {
return { ok: false, error: 'Could not reach ClawSuite server' }
return { ok: false, error: 'Could not reach Hermes Workspace server' }
}
}
@@ -354,7 +354,7 @@ export const useGatewaySetupStore = create<GatewaySetupState>((set, get) => ({
set({
testStatus: 'error',
testError: error || 'Gateway not reachable after saving config. You may need to restart ClawSuite.',
testError: error || 'Gateway not reachable after saving config. You may need to restart Hermes Workspace.',
})
return false
},

View File

@@ -1,10 +1,10 @@
import { useEffect } from 'react'
const BASE_TITLE = 'ClawSuite'
const BASE_TITLE = 'Hermes Workspace'
/**
* Sets document.title for the current page.
* Usage: usePageTitle('Dashboard') → "Dashboard — ClawSuite"
* Usage: usePageTitle('Dashboard') → "Dashboard — Hermes Workspace"
*/
export function usePageTitle(page: string) {
useEffect(() => {

View File

@@ -33,7 +33,7 @@ export const defaultStudioSettings: StudioSettings = {
gatewayUrl: '',
gatewayToken: '',
theme: 'system',
accentColor: 'orange',
accentColor: 'blue',
editorFontSize: 13,
editorWordWrap: true,
editorMinimap: false,
@@ -121,8 +121,8 @@ export function applyTheme(theme: SettingsThemeMode) {
if (resolvedDark) {
// Preserve user's enterprise dark theme if set, otherwise default to ops-dark
const stored = localStorage.getItem('clawsuite-theme')
const darkThemes = ['ops-dark', 'premium-dark', 'sunset-brand']
root.setAttribute('data-theme', darkThemes.includes(stored ?? '') ? (stored as string) : 'ops-dark')
const darkThemes = ['ops-dark', 'premium-dark', 'sunset-brand', 'hermes']
root.setAttribute('data-theme', darkThemes.includes(stored ?? '') ? (stored as string) : 'hermes')
} else {
root.setAttribute('data-theme', 'paper-light')
}

View File

@@ -1,5 +1,5 @@
/**
* React hook for sound notifications in ClawSuite
* React hook for sound notifications in Hermes Workspace
* Integrates with the agent swarm store to auto-play sounds on state changes
*/
import { useCallback, useEffect, useMemo, useRef } from 'react'

View File

@@ -66,7 +66,7 @@ export function getConnectionErrorMessage(
switch (kind) {
case 'clawsuite_auth_required':
return {
title: 'ClawSuite Login Required',
title: 'Hermes Workspace Login Required',
description: 'This instance requires a password to access.',
action: 'Enter your password to continue',
}
@@ -87,7 +87,7 @@ export function getConnectionErrorMessage(
case 'gateway_unreachable':
return {
title: 'Gateway unreachable',
description: 'ClawSuite cannot reach the configured OpenClaw gateway.',
description: 'Hermes Workspace cannot reach the configured OpenClaw gateway.',
action: 'Check that OpenClaw is running and the gateway URL is correct.',
}
case 'handshake_failed':

View File

@@ -14,8 +14,8 @@ const PLATFORM_NAMES: Record<string, string> = {
whatsapp: 'WhatsApp',
signal: 'Signal',
imessage: 'iMessage',
webchat: 'ClawSuite',
'openclaw-control-ui': 'ClawSuite',
webchat: 'Hermes Workspace',
'openclaw-control-ui': 'Hermes Workspace',
slack: 'Slack',
irc: 'IRC',
googlechat: 'Google Chat',

View File

@@ -1,5 +1,5 @@
/**
* Sound Notification System for ClawSuite
* Sound Notification System for Hermes Workspace
* Uses Web Audio API to synthesize unique sounds without audio files.
*/

View File

@@ -3,13 +3,15 @@ export type ThemeId =
| 'ops-dark'
| 'premium-dark'
| 'sunset-brand'
| 'hermes'
const DARK_THEMES: ThemeId[] = ['ops-dark', 'premium-dark', 'sunset-brand']
const DARK_THEMES: ThemeId[] = ['ops-dark', 'premium-dark', 'sunset-brand', 'hermes']
const THEME_SET = new Set<ThemeId>([
'paper-light',
'ops-dark',
'premium-dark',
'sunset-brand',
'hermes',
])
export const THEMES: Array<{
@@ -18,48 +20,25 @@ export const THEMES: Array<{
description: string
icon: string
}> = [
{
id: 'paper-light',
label: 'Paper Light',
description: 'Clean warm gray with soft shadows',
icon: '☀️',
},
{
id: 'ops-dark',
label: 'Ops Dark',
description: 'Slate dark with teal secondary accents',
icon: '🖥️',
},
{
id: 'premium-dark',
label: 'Premium Dark',
description: 'OLED black with high contrast',
icon: '✨',
},
{
id: 'sunset-brand',
label: 'Sunset Brand',
description: 'Warm brown immersion with amber accents',
icon: '🌇',
},
{ id: 'paper-light', label: 'Paper Light', description: 'Clean warm gray with soft shadows', icon: '☀️' },
{ id: 'ops-dark', label: 'Ops Dark', description: 'Slate dark with teal secondary accents', icon: '🖥️' },
{ id: 'premium-dark', label: 'Premium Dark', description: 'OLED black with high contrast', icon: '✨' },
{ id: 'sunset-brand', label: 'Sunset Brand', description: 'Warm brown immersion with amber accents', icon: '🌇' },
{ id: 'hermes', label: 'Hermes', description: 'Nous Research deep blue palette', icon: '⚕' },
]
const STORAGE_KEY = 'clawsuite-theme'
export function getStoredTheme(): ThemeId {
if (typeof window === 'undefined') return 'paper-light'
if (typeof window === 'undefined') return 'hermes'
const stored = localStorage.getItem(STORAGE_KEY)
if (stored && THEME_SET.has(stored as ThemeId)) {
return stored as ThemeId
}
return 'paper-light'
if (stored && THEME_SET.has(stored as ThemeId)) return stored as ThemeId
return 'hermes'
}
export function applyTheme(theme: ThemeId): void {
const html = document.documentElement
html.setAttribute('data-theme', theme)
// Also toggle dark class for Tailwind dark: variant
if (DARK_THEMES.includes(theme)) {
html.classList.add('dark')
html.classList.remove('light')
@@ -67,7 +46,6 @@ export function applyTheme(theme: ThemeId): void {
html.classList.add('light')
html.classList.remove('dark')
}
localStorage.setItem(STORAGE_KEY, theme)
}

View File

@@ -39,8 +39,8 @@ const themeScript = `
(() => {
window.process = window.process || { env: {}, platform: 'browser' };
// Gateway connection via ClawSuite server proxy.
// Clients connect to /ws-gateway on the ClawSuite server (same host:port as the page).
// Gateway connection via Hermes Workspace server proxy.
// Clients connect to /ws-gateway on the Hermes Workspace server (same host:port as the page).
// The server proxies internally to ws://127.0.0.1:18789 — so phone/LAN/Docker
// users never need direct access to port 18789.
// Manual override: set gatewayUrl in settings to skip proxy (e.g. wss:// remote).
@@ -52,7 +52,7 @@ const themeScript = `
if (manualUrl && typeof manualUrl === 'string' && manualUrl.startsWith('ws')) {
window.__GATEWAY_URL__ = manualUrl
} else {
// Use proxy path — works from any device that can reach ClawSuite
// Use proxy path — works from any device that can reach Hermes Workspace
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
window.__GATEWAY_URL__ = proto + '//' + window.location.host + '/ws-gateway'
}
@@ -89,7 +89,7 @@ const themeScript = `
}
const root = document.documentElement
const media = window.matchMedia('(prefers-color-scheme: dark)')
// ClawSuite theme class + data-theme attribute
// Hermes Workspace theme class + data-theme attribute
const enterpriseTheme = localStorage.getItem('clawsuite-theme')
const isValidEnterpriseTheme =
enterpriseTheme === 'ops-dark' ||
@@ -169,7 +169,7 @@ export const Route = createRootRoute({
'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-visual',
},
{
title: 'ClawSuite',
title: 'Hermes Workspace',
},
{
name: 'description',
@@ -340,7 +340,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
d.id = 'splash-screen';
d.style.cssText = 'position:fixed;inset:0;z-index:99999;display:flex;flex-direction:column;align-items:center;justify-content:center;background:'+bg+';transition:opacity 0.8s ease;';
d.innerHTML = '<div style="width:96px;height:96px;margin-bottom:20px;filter:drop-shadow(0 8px 32px rgba(249,115,22,0.5))"><svg width="96" height="96" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sOB" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#ea580c"/><stop offset="50%" stop-color="#f97316"/><stop offset="100%" stop-color="#fb923c"/></linearGradient></defs><rect x="5" y="5" width="90" height="90" rx="16" fill="url(#sOB)"/><rect x="20" y="25" width="60" height="50" rx="4" stroke="#1e293b" stroke-width="3" fill="none"/><circle cx="28" cy="32" r="2.5" fill="#1e293b"/><circle cx="37" cy="32" r="2.5" fill="#1e293b"/><circle cx="46" cy="32" r="2.5" fill="#1e293b"/><path d="M38 45L32 50L38 55" stroke="#1e293b" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><path d="M62 45L68 50L62 55" stroke="#1e293b" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/><rect x="47" y="46" width="4" height="10" rx="2" fill="#1e293b"><animate attributeName="opacity" values="1;0.3;1" dur="1.2s" repeatCount="indefinite"/></rect></svg></div>'
+ '<div style="font:700 24px/1 system-ui,-apple-system,sans-serif;letter-spacing:0.06em;color:'+txt+'">ClawSuite</div>'
+ '<div style="font:700 24px/1 system-ui,-apple-system,sans-serif;letter-spacing:0.06em;color:'+txt+'">Hermes Workspace</div>'
+ '<div style="margin-top:10px;font:italic 13px/1 system-ui,-apple-system,sans-serif;color:'+muted+'">'+quip+'</div>'
+ '<div style="margin-top:28px;width:140px;height:3px;background:#1e293b;border-radius:3px;overflow:hidden"><div id=splash-bar style="width:0%;height:100%;background:linear-gradient(90deg,#ea580c,#f97316,#fb923c);border-radius:3px;transition:width 0.4s ease"></div></div>';
document.body.prepend(d);

View File

@@ -23,7 +23,7 @@ export const Route = createFileRoute('/api/gateway/approvals/$approvalId/$action
)
}
// Map ClawSuite action names to gateway ExecApprovalDecision values.
// Map Hermes Workspace action names to gateway ExecApprovalDecision values.
const decision = action === 'approve' ? 'allow-once' : 'deny'
try {

View File

@@ -11,7 +11,7 @@ function ConnectRoute() {
<div className="max-w-2xl mx-auto px-6 py-10 space-y-10">
<div className="space-y-3">
<h1 className="text-3xl font-medium tracking-[-0.02em] text-center mb-10">
Connect to ClawSuite
Connect to Hermes Workspace
</h1>
<p className="text-primary-700">
This client needs access to your OpenClaw gateway before you can

View File

@@ -1615,7 +1615,7 @@ function ChatComposerComponent({
isDraggingOver &&
'outline-primary-500 ring-2 ring-primary-300 bg-primary-50/80',
isLoading &&
'ring-2 ring-accent-400/70 shadow-[0_0_20px_rgba(249,115,22,0.35)] animate-pulse-glow',
'ring-2 ring-accent-400/70 shadow-[0_0_20px_rgba(48,80,255,0.35)] animate-pulse-glow',
)}
onPaste={handlePaste}
onDragEnter={handleDragEnter}

View File

@@ -47,7 +47,6 @@ import {
} from '@/components/ui/tooltip'
import { cn } from '@/lib/utils'
import { Button, buttonVariants } from '@/components/ui/button'
import { OpenClawStudioIcon } from '@/components/icons/clawsuite'
import { UserAvatar } from '@/components/avatars'
import { SEARCH_MODAL_EVENTS, useSearchModal } from '@/hooks/use-search-modal'
import {
@@ -1099,8 +1098,8 @@ function ChatSidebarComponent({
'w-full pl-1.5 justify-start',
)}
>
<OpenClawStudioIcon className="size-5 rounded-lg overflow-hidden" />
ClawSuite
<img src="/hermes-icon.png" alt="Hermes" className="size-5 rounded-lg" />
Hermes Workspace
</Link>
</motion.div>
) : null}

View File

@@ -25,7 +25,7 @@ function classifyConnectionError(
if (!normalizedError && !status) {
return {
title: 'Not connected',
description: "ClawSuite can't reach the gateway.",
description: "Hermes Workspace can't reach the gateway.",
action: 'Check that OpenClaw is running, then try again.',
}
}

View File

@@ -80,7 +80,7 @@ export function useChatSessions({
if (activeSession.titleStatus === 'error') return 'New Session'
return 'New Session'
}
return activeFriendlyId === 'main' ? 'ClawSuite' : activeFriendlyId
return activeFriendlyId === 'main' ? 'Hermes Workspace' : activeFriendlyId
}, [activeFriendlyId, activeSession])
const sessionsError =

View File

@@ -45,7 +45,6 @@ import { useVisibleWidgets } from './hooks/use-visible-widgets'
import { useDashboardData, buildUsageSummaryText } from './hooks/use-dashboard-data'
import { formatMoney, formatRelativeTime } from './lib/formatters'
import { ErrorBoundary } from '@/components/error-boundary'
import { OpenClawStudioIcon } from '@/components/icons/clawsuite'
import { ThemeToggle } from '@/components/theme-toggle'
import { SettingsDialog } from '@/components/settings-dialog'
import { DashboardOverflowPanel } from '@/components/dashboard-overflow-panel'
@@ -643,7 +642,7 @@ export function DashboardScreen() {
className="shrink-0 cursor-pointer rounded-xl transition-transform active:scale-95"
aria-label="Open quick menu"
>
<OpenClawStudioIcon className="size-8 rounded-xl overflow-hidden shadow-sm" />
<img src="/hermes-icon.png" alt="Hermes" className="size-8 rounded-xl shadow-sm" />
{shouldShowLogoTip ? (
<div className="absolute !left-1/2 top-full z-30 mt-2 -translate-x-1/2 animate-in fade-in-0 slide-in-from-top-1 duration-300">
<div className="relative rounded bg-primary-900 px-2 py-1 text-xs font-medium text-white shadow-md">
@@ -663,11 +662,11 @@ export function DashboardScreen() {
) : null}
</button>
) : (
<OpenClawStudioIcon className="size-8 shrink-0 rounded-xl overflow-hidden shadow-sm" />
<img src="/hermes-icon.png" alt="Hermes" className="size-8 shrink-0 rounded-xl shadow-sm" />
)}
<div className="flex min-w-0 items-center gap-2">
<h1 className="text-sm font-semibold text-ink text-balance md:text-base truncate">
ClawSuite
Hermes Workspace
</h1>
{isMobile ? (
<span
@@ -699,6 +698,11 @@ export function DashboardScreen() {
{dashboardData.connection.connected ? 'Connected' : 'Disconnected'}
</span>
)}
{!isMobile && (
<span className="inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] font-medium" style={{ borderColor: 'var(--theme-accent-border)', color: 'var(--theme-accent)', background: 'var(--theme-accent-subtle)' }}>
Powered by Hermes Agent
</span>
)}
</div>
</div>

View File

@@ -125,7 +125,7 @@ export function useServicesHealth(gatewayConnected: boolean) {
return [
{
name: 'ClawSuite UI',
name: 'Hermes Workspace UI',
status: isChecking ? 'checking' : (probe?.clawSuiteUi.status ?? 'down'),
latencyMs: probe?.clawSuiteUi.latencyMs,
},

View File

@@ -69,7 +69,7 @@ function generateMarkdown(report: ExportableMissionReport): string {
}
lines.push('---')
lines.push(`*Exported from ClawSuite Agent Hub on ${new Date().toLocaleString()}*`)
lines.push(`*Exported from Hermes Workspace Agent Hub on ${new Date().toLocaleString()}*`)
return lines.join('\n')
}

View File

@@ -544,7 +544,7 @@ export function OfficeView({
{/* Header bar */}
<div className="flex shrink-0 flex-wrap items-start justify-between gap-2 border-b border-neutral-200 bg-white/80 px-5 py-3 backdrop-blur dark:border-slate-700 dark:bg-slate-800/80">
<div className="flex min-w-0 flex-1 flex-col gap-1">
<span className="text-base font-bold text-neutral-900 dark:text-white">ClawSuite Office</span>
<span className="text-base font-bold text-neutral-900 dark:text-white">Hermes Workspace Office</span>
<div className="flex flex-wrap items-center gap-2">
<span className="rounded-full bg-neutral-100 dark:bg-neutral-800 px-2 py-0.5 text-[10px] font-medium text-neutral-600 dark:text-neutral-400 tabular-nums">{agentRows.length} agents</span>
<span className="rounded-full bg-emerald-50 dark:bg-emerald-900/30 px-2 py-0.5 text-[10px] font-medium text-emerald-700 dark:text-emerald-400 tabular-nums">{activeCount} working</span>

View File

@@ -17,7 +17,7 @@ export type WorkflowTemplate = {
const STORAGE_KEY = 'clawsuite:workflow-templates'
// Built-in templates that ship with ClawSuite
// Built-in templates that ship with Hermes Workspace
export const BUILT_IN_TEMPLATES: WorkflowTemplate[] = [
{
id: 'tpl-code-review',

View File

@@ -670,7 +670,7 @@ export function NewProjectWizardContent({
<input
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="ClawSuite Workspace Refresh"
placeholder="Hermes Workspace Workspace Refresh"
autoFocus
className="w-full rounded-2xl border border-primary-200 bg-white px-4 py-3 text-sm text-primary-900 outline-none transition-colors focus:border-accent-500"
/>

View File

@@ -270,7 +270,7 @@ export function SkillsScreen() {
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="space-y-1.5">
<p className="text-xs font-medium uppercase text-primary-500 tabular-nums">
ClawSuite Marketplace
Hermes Workspace Marketplace
</p>
<h1 className="text-2xl font-medium text-ink text-balance sm:text-3xl">
Skills Browser
@@ -657,7 +657,7 @@ function SecurityScanCard({ security }: { security: SecurityRisk }) {
<div className="space-y-1.5">
<div className="flex items-center gap-2">
<span className="text-primary-500 font-medium w-16 shrink-0">
ClawSuite
Hermes Workspace
</span>
<span
className={cn(

View File

@@ -97,7 +97,7 @@ function getDemoTabs(): Array<BrowserTab> {
return [
{
id: 'demo-tab-1',
title: 'ClawSuite',
title: 'Hermes Workspace',
url: 'https://openclaw.local/studio',
isActive: true,
},

View File

@@ -1,6 +1,6 @@
/**
* Local web proxy that strips iframe-blocking headers (X-Frame-Options, CSP).
* Allows any website to be embedded in an iframe inside ClawSuite.
* Allows any website to be embedded in an iframe inside Hermes Workspace.
* Only runs locally — not exposed to the internet.
*/

View File

@@ -47,7 +47,7 @@ export async function launchBrowser(): Promise<BrowserState> {
isLaunching = true
try {
const pw = await getPlaywright()
// Headless browser — rendered inside ClawSuite via CDP screencast
// Headless browser — rendered inside Hermes Workspace via CDP screencast
browserInstance = await pw.chromium.launch({
headless: true,
args: [

View File

@@ -16,7 +16,7 @@ type ProviderConfig = {
apiKey?: string
}
type ClawSuiteConfig = {
type HermesWorkspaceConfig = {
models?: {
providers?: {
anthropic?: ProviderConfig
@@ -109,7 +109,7 @@ function buildPrompt(terminalOutput: string, logContent: string): string {
}
function readProviderApiKey(
config: ClawSuiteConfig,
config: HermesWorkspaceConfig,
provider: 'anthropic' | 'openai',
): string {
const providerConfig = config.models?.providers?.[provider]
@@ -129,12 +129,12 @@ function readProviderApiKey(
return ''
}
async function readClawSuiteConfig(): Promise<ClawSuiteConfig | null> {
async function readHermesWorkspaceConfig(): Promise<HermesWorkspaceConfig | null> {
const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json')
try {
const raw = await fs.readFile(configPath, 'utf8')
return JSON.parse(raw) as ClawSuiteConfig
return JSON.parse(raw) as HermesWorkspaceConfig
} catch {
return null
}
@@ -167,7 +167,7 @@ async function resolveProvider(): Promise<ResolvedProvider | null> {
return { provider: 'openai', apiKey: openaiEnv }
}
const config = await readClawSuiteConfig()
const config = await readHermesWorkspaceConfig()
if (!config) return null
const anthropicApiKey = readProviderApiKey(config, 'anthropic')

View File

@@ -6,7 +6,7 @@
* 2. Falling back to `openclaw config get` CLI commands
* 3. Probing default port 18789
*
* This lets ClawSuite connect seamlessly without manual config.
* This lets Hermes Workspace connect seamlessly without manual config.
*/
import { readFile } from 'node:fs/promises'

View File

@@ -270,7 +270,7 @@ export async function fetchClaudeUsage(): Promise<ProviderUsageResult> {
Accept: 'application/json',
'Content-Type': 'application/json',
'anthropic-beta': 'oauth-2025-04-20',
'User-Agent': 'ClawSuite',
'User-Agent': 'Hermes Workspace',
},
})
} catch (e) {
@@ -295,7 +295,7 @@ export async function fetchClaudeUsage(): Promise<ProviderUsageResult> {
Accept: 'application/json',
'Content-Type': 'application/json',
'anthropic-beta': 'oauth-2025-04-20',
'User-Agent': 'ClawSuite',
'User-Agent': 'Hermes Workspace',
},
})
}
@@ -568,7 +568,7 @@ export async function fetchCodexUsage(): Promise<ProviderUsageResult> {
const headers: Record<string, string> = {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/json',
'User-Agent': 'ClawSuite',
'User-Agent': 'Hermes Workspace',
}
if (auth.tokens.account_id) {
headers['ChatGPT-Account-Id'] = auth.tokens.account_id

View File

@@ -458,7 +458,7 @@ export const useGatewayChatStore = create<GatewayChatState>((set, get) => ({
// Content-text dedup: identical assistant text within the same
// session should never appear twice, even if message IDs differ
// (e.g. same reply routed from Telegram + ClawSuite).
// (e.g. same reply routed from Telegram + Hermes Workspace).
if (
normalizedMessage.role === 'assistant' &&
newPlainText.length > 20 &&

View File

@@ -383,54 +383,54 @@
color: var(--theme-text);
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.border-primary-200, .border-neutral-200, .border-neutral-700, .border-neutral-800, .border-slate-700) {
border-color: var(--theme-border) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.bg-primary-50, .bg-neutral-50, .bg-slate-800) {
background-color: var(--theme-panel) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.bg-white, .bg-primary-100, .bg-neutral-800, .bg-neutral-900) {
background-color: var(--theme-card) !important;
}
:is([data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.bg-neutral-900\/60, .bg-neutral-900\/40) {
background-color: var(--theme-card2) !important;
opacity: 1 !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.text-primary-900, .text-neutral-100, .text-neutral-200) {
color: var(--theme-text) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.text-primary-500, .text-primary-600, .text-neutral-300, .text-neutral-400, .text-neutral-500) {
color: var(--theme-muted) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is(.ring-orange-300, .ring-accent-500\/20, .border-accent-500) {
--tw-ring-color: var(--theme-accent) !important;
border-color: var(--theme-accent) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
:is([aria-selected="true"], [data-selected="true"], .border-primary-500) {
border-color: var(--theme-accent) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
.bg-surface {
background-color: var(--theme-bg) !important;
}
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"])
:is([data-theme="paper-light"], [data-theme="ops-dark"], [data-theme="premium-dark"], [data-theme="sunset-brand"], [data-theme="hermes"])
.text-ink {
color: var(--theme-text) !important;
}
@@ -1055,13 +1055,46 @@ code {
/* Orange glow pulse on composer when agent is actively working */
@keyframes pulse-glow {
0%, 100% {
box-shadow: 0 0 12px rgba(249, 115, 22, 0.25);
box-shadow: 0 0 12px var(--theme-accent-subtle, rgba(48, 80, 255, 0.25));
}
50% {
box-shadow: 0 0 28px rgba(249, 115, 22, 0.55);
box-shadow: 0 0 28px var(--theme-accent-border, rgba(48, 80, 255, 0.55));
}
}
.animate-pulse-glow {
animation: pulse-glow 2s ease-in-out infinite;
}
/* ── Hermes / Nous Research ── */
[data-theme="hermes"] {
--theme-bg: #0A0E1A;
--theme-sidebar: #080C16;
--theme-panel: #12182A;
--theme-card: #1A2240;
--theme-card2: #202850;
--theme-border: rgba(48, 80, 255, 0.18);
--theme-border-subtle: rgba(48, 80, 255, 0.1);
--theme-text: #E8ECFF;
--theme-muted: #8090BB;
--theme-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.55);
--theme-shadow-2: 0 6px 16px rgba(0, 0, 0, 0.52);
--theme-shadow-3: 0 16px 36px rgba(0, 0, 0, 0.62);
--theme-glass: rgba(10, 14, 26, 0.88);
--theme-focus: #3050FF;
--theme-accent: #3050FF;
--theme-accent-secondary: #5070FF;
--theme-accent-subtle: rgba(48, 80, 255, 0.14);
--theme-accent-border: rgba(48, 80, 255, 0.32);
--theme-stripe: rgba(48, 80, 255, 0.06);
--theme-header-bg: #080C16;
--theme-header-border: rgba(48, 80, 255, 0.18);
}
[data-theme="hermes"] html,
[data-theme="hermes"] body,
[data-theme="hermes"] #root,
[data-theme="hermes"] .dark {
background-color: var(--theme-bg);
color: var(--theme-text);
}