feat(06-02): replace Paperclip brand + CEO display strings in UI components

- AgentDetail: 10 strings replaced (Paperclip→VOCAB.appName, CEO→VOCAB.ceo, board approval→owner approval)
- RoutineDetail: 8 error messages + select company + secret banner replaced
- DesignGuide: 3 strings replaced (Paperclip, Paperclip App, CEO Agent)
- agent-config-primitives: 3 tooltip strings replaced
- AccountingModelCard, JsonSchemaForm, ProjectProperties, OnboardingWizard: 1 each
- openclaw-gateway/config-fields: 2 strings replaced
- Added VOCAB import to all files missing it
This commit is contained in:
Mikkel Georgsen 2026-03-31 13:25:46 +02:00
parent ccc8ead357
commit f7cbd2074f
10 changed files with 40 additions and 33 deletions

View file

@ -1,4 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import { Eye, EyeOff } from "lucide-react"; import { Eye, EyeOff } from "lucide-react";
import type { AdapterConfigFieldsProps } from "../types"; import type { AdapterConfigFieldsProps } from "../types";
import { import {
@ -134,7 +135,7 @@ export function OpenClawGatewayConfigFields({
{!isCreate && ( {!isCreate && (
<> <>
<Field label="Paperclip API URL override"> <Field label={`${VOCAB.appName} API URL override`}>
<DraftInput <DraftInput
value={ value={
eff( eff(
@ -226,7 +227,7 @@ export function OpenClawGatewayConfigFields({
<Field label="Device auth"> <Field label="Device auth">
<div className="text-xs text-muted-foreground leading-relaxed"> <div className="text-xs text-muted-foreground leading-relaxed">
Always enabled for gateway agents. Paperclip persists a device key during onboarding so pairing approvals {`Always enabled for gateway agents. ${VOCAB.appName} persists a device key during onboarding so pairing approvals`}
remain stable across runs. remain stable across runs.
</div> </div>
</Field> </Field>

View file

@ -1,4 +1,5 @@
import { Database, Gauge, ReceiptText } from "lucide-react"; import { Database, Gauge, ReceiptText } from "lucide-react";
import { VOCAB } from "@paperclipai/branding";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
const SURFACES = [ const SURFACES = [
@ -34,7 +35,7 @@ export function AccountingModelCard() {
Accounting model Accounting model
</CardTitle> </CardTitle>
<CardDescription className="max-w-2xl text-sm leading-6"> <CardDescription className="max-w-2xl text-sm leading-6">
Paperclip now separates request-level inference usage from account-level finance events. {`${VOCAB.appName} now separates request-level inference usage from account-level finance events.`}
That keeps provider reporting honest when the biller is OpenRouter, Cloudflare, Bedrock, or another intermediary. That keeps provider reporting honest when the biller is OpenRouter, Cloudflare, Bedrock, or another intermediary.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>

View file

@ -1,4 +1,5 @@
import React, { useCallback, useMemo, useState } from "react"; import React, { useCallback, useMemo, useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import { import {
ChevronDown, ChevronDown,
ChevronRight, ChevronRight,
@ -494,7 +495,7 @@ const SecretField = React.memo(({
label={label} label={label}
description={ description={
description || description ||
"This secret is stored securely via the Paperclip secret provider." `This secret is stored securely via the ${VOCAB.appName} secret provider.`
} }
required={isRequired} required={isRequired}
error={error} error={error}

View file

@ -1063,7 +1063,7 @@ export function OnboardingWizard() {
<p className="text-[11px] text-amber-900/90 leading-relaxed"> <p className="text-[11px] text-amber-900/90 leading-relaxed">
Claude failed while{" "} Claude failed while{" "}
<span className="font-mono">ANTHROPIC_API_KEY</span>{" "} <span className="font-mono">ANTHROPIC_API_KEY</span>{" "}
is set. You can clear it in this CEO adapter config is set. You can clear it in this {VOCAB.ceo} adapter config
and retry the probe. and retry the probe.
</p> </p>
<Button <Button

View file

@ -1,4 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import { Link } from "@/lib/router"; import { Link } from "@/lib/router";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { Project } from "@paperclipai/shared"; import type { Project } from "@paperclipai/shared";
@ -687,7 +688,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
{codebase.effectiveLocalFolder} {codebase.effectiveLocalFolder}
</div> </div>
{codebase.origin === "managed_checkout" && ( {codebase.origin === "managed_checkout" && (
<div className="text-[11px] text-muted-foreground">Paperclip-managed folder.</div> <div className="text-[11px] text-muted-foreground">{`${VOCAB.appName}-managed folder.`}</div>
)} )}
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
@ -719,7 +720,7 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa
{hasAdditionalLegacyWorkspaces && ( {hasAdditionalLegacyWorkspaces && (
<div className="text-[11px] text-muted-foreground"> <div className="text-[11px] text-muted-foreground">
Additional legacy workspace records exist on this project. Paperclip is using the primary workspace as the codebase view. {`Additional legacy workspace records exist on this project. ${VOCAB.appName} is using the primary workspace as the codebase view.`}
</div> </div>
)} )}

View file

@ -1,4 +1,5 @@
import { useState, useRef, useEffect, useCallback } from "react"; import { useState, useRef, useEffect, useCallback } from "react";
import { VOCAB } from "@paperclipai/branding";
import { import {
Tooltip, Tooltip,
TooltipTrigger, TooltipTrigger,
@ -33,7 +34,7 @@ export const help: Record<string, string> = {
dangerouslySkipPermissions: "Run unattended by auto-approving adapter permission prompts when supported.", dangerouslySkipPermissions: "Run unattended by auto-approving adapter permission prompts when supported.",
dangerouslyBypassSandbox: "Run Codex without sandbox restrictions. Required for filesystem/network access.", dangerouslyBypassSandbox: "Run Codex without sandbox restrictions. Required for filesystem/network access.",
search: "Enable Codex web search capability during runs.", search: "Enable Codex web search capability during runs.",
workspaceStrategy: "How Paperclip should realize an execution workspace for this agent. Keep project_primary for normal cwd execution, or use git_worktree for issue-scoped isolated checkouts.", workspaceStrategy: `How ${VOCAB.appName} should realize an execution workspace for this agent. Keep project_primary for normal cwd execution, or use git_worktree for issue-scoped isolated checkouts.`,
workspaceBaseRef: "Base git ref used when creating a worktree branch. Leave blank to use the resolved workspace ref or HEAD.", workspaceBaseRef: "Base git ref used when creating a worktree branch. Leave blank to use the resolved workspace ref or HEAD.",
workspaceBranchTemplate: "Template for naming derived branches. Supports {{issue.identifier}}, {{issue.title}}, {{agent.name}}, {{project.id}}, {{workspace.repoRef}}, and {{slug}}.", workspaceBranchTemplate: "Template for naming derived branches. Supports {{issue.identifier}}, {{issue.title}}, {{agent.name}}, {{project.id}}, {{workspace.repoRef}}, and {{slug}}.",
worktreeParentDir: "Directory where derived worktrees should be created. Absolute, ~-prefixed, and repo-relative paths are supported.", worktreeParentDir: "Directory where derived worktrees should be created. Absolute, ~-prefixed, and repo-relative paths are supported.",
@ -44,8 +45,8 @@ export const help: Record<string, string> = {
args: "Command-line arguments, comma-separated.", args: "Command-line arguments, comma-separated.",
extraArgs: "Extra CLI arguments for local adapters, comma-separated.", extraArgs: "Extra CLI arguments for local adapters, comma-separated.",
envVars: "Environment variables injected into the adapter process. Use plain values or secret references.", envVars: "Environment variables injected into the adapter process. Use plain values or secret references.",
bootstrapPrompt: "Only sent when Paperclip starts a fresh session. Use this for stable setup guidance that should not be repeated on every heartbeat.", bootstrapPrompt: `Only sent when ${VOCAB.appName} starts a fresh session. Use this for stable setup guidance that should not be repeated on every heartbeat.`,
payloadTemplateJson: "Optional JSON merged into remote adapter request payloads before Paperclip adds its standard wake and workspace fields.", payloadTemplateJson: `Optional JSON merged into remote adapter request payloads before ${VOCAB.appName} adds its standard wake and workspace fields.`,
webhookUrl: "The URL that receives POST requests when the agent is invoked.", webhookUrl: "The URL that receives POST requests when the agent is invoked.",
heartbeatInterval: "Run this agent automatically on a timer. Useful for periodic tasks like checking for new work.", heartbeatInterval: "Run this agent automatically on a timer. Useful for periodic tasks like checking for new work.",
intervalSec: "Seconds between automatic heartbeat invocations.", intervalSec: "Seconds between automatic heartbeat invocations.",

View file

@ -925,7 +925,7 @@ export function AgentDetail() {
{actionError && <p className="text-sm text-destructive">{actionError}</p>} {actionError && <p className="text-sm text-destructive">{actionError}</p>}
{isPendingApproval && ( {isPendingApproval && (
<p className="text-sm text-amber-500"> <p className="text-sm text-amber-500">
This agent is pending board approval and cannot be invoked yet. This agent is pending owner approval and cannot be invoked yet.
</p> </p>
)} )}
@ -1497,11 +1497,11 @@ function ConfigurationTab({
const taskAssignLocked = agent.role === "ceo" || canCreateAgents; const taskAssignLocked = agent.role === "ceo" || canCreateAgents;
const taskAssignHint = const taskAssignHint =
taskAssignSource === "ceo_role" taskAssignSource === "ceo_role"
? "Enabled automatically for CEO agents." ? `Enabled automatically for ${VOCAB.ceo} agents.`
: taskAssignSource === "agent_creator" : taskAssignSource === "agent_creator"
? "Enabled automatically while this agent can create new agents." ? "Enabled automatically while this agent can create new agents."
: taskAssignSource === "explicit_grant" : taskAssignSource === "explicit_grant"
? "Enabled via explicit company permission grant." ? `Enabled via explicit ${VOCAB.company.toLowerCase()} permission grant.`
: "Disabled unless explicitly granted."; : "Disabled unless explicitly granted.";
return ( return (
@ -1945,7 +1945,7 @@ function PromptsTab({
<HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" /> <HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="right" sideOffset={4}> <TooltipContent side="right" sideOffset={4}>
Managed: Paperclip stores and serves the instructions bundle. External: you provide a path on disk where the instructions live. {`Managed: ${VOCAB.appName} stores and serves the instructions bundle. External: you provide a path on disk where the instructions live.`}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</span> </span>
@ -2000,7 +2000,7 @@ function PromptsTab({
<HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" /> <HelpCircle className="h-3 w-3 text-muted-foreground cursor-help" />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent side="right" sideOffset={4}> <TooltipContent side="right" sideOffset={4}>
The absolute directory on disk where the instructions bundle lives. In managed mode this is set by Paperclip automatically. {`The absolute directory on disk where the instructions bundle lives. In managed mode this is set by ${VOCAB.appName} automatically.`}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</span> </span>
@ -2531,9 +2531,9 @@ function AgentSkillsTab({
const unsupportedSkillMessage = useMemo(() => { const unsupportedSkillMessage = useMemo(() => {
if (skillSnapshot?.mode !== "unsupported") return null; if (skillSnapshot?.mode !== "unsupported") return null;
if (agent.adapterType === "openclaw_gateway") { if (agent.adapterType === "openclaw_gateway") {
return "Paperclip cannot manage OpenClaw skills here. Visit your OpenClaw instance to manage this agent's skills."; return `${VOCAB.appName} cannot manage OpenClaw skills here. Visit your OpenClaw instance to manage this agent's skills.`;
} }
return "Paperclip cannot manage skills for this adapter yet. Manage them in the adapter directly."; return `${VOCAB.appName} cannot manage skills for this adapter yet. Manage them in the adapter directly.`;
}, [agent.adapterType, skillSnapshot?.mode]); }, [agent.adapterType, skillSnapshot?.mode]);
const hasUnsavedChanges = !arraysEqual(skillDraft, lastSavedSkills); const hasUnsavedChanges = !arraysEqual(skillDraft, lastSavedSkills);
const saveStatusLabel = syncSkills.isPending const saveStatusLabel = syncSkills.isPending
@ -2691,7 +2691,7 @@ function AgentSkillsTab({
<section className="border-y border-border"> <section className="border-y border-border">
<div className="border-b border-border bg-muted/40 px-3 py-2"> <div className="border-b border-border bg-muted/40 px-3 py-2">
<span className="text-xs font-medium text-muted-foreground"> <span className="text-xs font-medium text-muted-foreground">
Required by Paperclip {`Required by ${VOCAB.appName}`}
</span> </span>
</div> </div>
{requiredSkillRows.map(renderSkillRow)} {requiredSkillRows.map(renderSkillRow)}
@ -2708,7 +2708,7 @@ function AgentSkillsTab({
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setUnmanagedOpen((v) => !v); } }} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setUnmanagedOpen((v) => !v); } }}
> >
<span className="text-xs font-medium text-muted-foreground"> <span className="text-xs font-medium text-muted-foreground">
({unmanagedSkillRows.length}) User-installed skills, not managed by Paperclip {`(${unmanagedSkillRows.length}) User-installed skills, not managed by ${VOCAB.appName}`}
</span> </span>
{unmanagedOpen ? <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" /> : <ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />} {unmanagedOpen ? <ChevronDown className="h-3.5 w-3.5 text-muted-foreground" /> : <ChevronRight className="h-3.5 w-3.5 text-muted-foreground" />}
</div> </div>
@ -3995,7 +3995,7 @@ function KeysTab({ agentId, companyId }: { agentId: string; companyId?: string }
Create API Key Create API Key
</h3> </h3>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
API keys allow this agent to authenticate calls to the Paperclip server. {`API keys allow this agent to authenticate calls to the ${VOCAB.appName} server.`}
</p> </p>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Input <Input

View file

@ -1,4 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import { import {
BookOpen, BookOpen,
Bot, Bot,
@ -194,7 +195,7 @@ export function DesignGuide() {
<div> <div>
<h2 className="text-xl font-bold">Design Guide</h2> <h2 className="text-xl font-bold">Design Guide</h2>
<p className="text-sm text-muted-foreground mt-1"> <p className="text-sm text-muted-foreground mt-1">
Every component, style, and pattern used across Paperclip. {`Every component, style, and pattern used across ${VOCAB.appName}.`}
</p> </p>
</div> </div>
@ -736,7 +737,7 @@ export function DesignGuide() {
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator /> <BreadcrumbSeparator />
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbLink href="#">Paperclip App</BreadcrumbLink> <BreadcrumbLink href="#">{`${VOCAB.appName} App`}</BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator /> <BreadcrumbSeparator />
<BreadcrumbItem> <BreadcrumbItem>
@ -943,7 +944,7 @@ export function DesignGuide() {
<SubSection title="Initials derivation"> <SubSection title="Initials derivation">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Identity name="CEO Agent" size="sm" /> <Identity name={`${VOCAB.ceo} Agent`} size="sm" />
<Identity name="Alpha" size="sm" /> <Identity name="Alpha" size="sm" />
<Identity name="Quality Assurance Lead" size="sm" /> <Identity name="Quality Assurance Lead" size="sm" />
</div> </div>

View file

@ -332,7 +332,7 @@ export function NewAgent() {
{/* Footer */} {/* Footer */}
<div className="border-t border-border px-4 py-3"> <div className="border-t border-border px-4 py-3">
{isFirstAgent && ( {isFirstAgent && (
<p className="text-xs text-muted-foreground mb-2">This will be the CEO</p> <p className="text-xs text-muted-foreground mb-2">{`This will be the ${VOCAB.ceo}`}</p>
)} )}
{formError && ( {formError && (
<p className="text-xs text-destructive mb-2">{formError}</p> <p className="text-xs text-destructive mb-2">{formError}</p>

View file

@ -1,4 +1,5 @@
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { VOCAB } from "@paperclipai/branding";
import { Link, useLocation, useNavigate, useParams } from "@/lib/router"; import { Link, useLocation, useNavigate, useParams } from "@/lib/router";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { import {
@ -402,7 +403,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Failed to save routine", title: "Failed to save routine",
body: error instanceof Error ? error.message : "Paperclip could not save the routine.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not save the routine.`,
tone: "error", tone: "error",
}); });
}, },
@ -423,7 +424,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Routine run failed", title: "Routine run failed",
body: error instanceof Error ? error.message : "Paperclip could not start the routine run.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not start the routine run.`,
tone: "error", tone: "error",
}); });
}, },
@ -445,7 +446,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Failed to update routine", title: "Failed to update routine",
body: error instanceof Error ? error.message : "Paperclip could not update the routine.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not update the routine.`,
tone: "error", tone: "error",
}); });
}, },
@ -486,7 +487,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Failed to add trigger", title: "Failed to add trigger",
body: error instanceof Error ? error.message : "Paperclip could not create the trigger.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not create the trigger.`,
tone: "error", tone: "error",
}); });
}, },
@ -504,7 +505,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Failed to update trigger", title: "Failed to update trigger",
body: error instanceof Error ? error.message : "Paperclip could not update the trigger.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not update the trigger.`,
tone: "error", tone: "error",
}); });
}, },
@ -522,7 +523,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Failed to delete trigger", title: "Failed to delete trigger",
body: error instanceof Error ? error.message : "Paperclip could not delete the trigger.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not delete the trigger.`,
tone: "error", tone: "error",
}); });
}, },
@ -544,7 +545,7 @@ export function RoutineDetail() {
onError: (error) => { onError: (error) => {
pushToast({ pushToast({
title: "Failed to rotate webhook secret", title: "Failed to rotate webhook secret",
body: error instanceof Error ? error.message : "Paperclip could not rotate the webhook secret.", body: error instanceof Error ? error.message : `${VOCAB.appName} could not rotate the webhook secret.`,
tone: "error", tone: "error",
}); });
}, },
@ -584,7 +585,7 @@ export function RoutineDetail() {
const currentProject = editDraft.projectId ? projectById.get(editDraft.projectId) ?? null : null; const currentProject = editDraft.projectId ? projectById.get(editDraft.projectId) ?? null : null;
if (!selectedCompanyId) { if (!selectedCompanyId) {
return <EmptyState icon={Repeat} message="Select a company to view routines." />; return <EmptyState icon={Repeat} message={`Select a ${VOCAB.company.toLowerCase()} to view routines.`} />;
} }
if (isLoading) { if (isLoading) {
@ -673,7 +674,7 @@ export function RoutineDetail() {
<div className="rounded-lg border border-blue-500/30 bg-blue-500/5 p-4 space-y-3 text-sm"> <div className="rounded-lg border border-blue-500/30 bg-blue-500/5 p-4 space-y-3 text-sm">
<div> <div>
<p className="font-medium">{secretMessage.title}</p> <p className="font-medium">{secretMessage.title}</p>
<p className="text-xs text-muted-foreground">Save this now. Paperclip will not show the secret value again.</p> <p className="text-xs text-muted-foreground">{`Save this now. ${VOCAB.appName} will not show the secret value again.`}</p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">