refactor(web): rename workspace persona to prompt

This commit is contained in:
Vorflux AI 2026-07-21 16:16:01 +00:00
parent 18780f1d09
commit 78017586e8
5 changed files with 36 additions and 33 deletions

View file

@ -42,7 +42,7 @@ import { useContainerTags } from "@/hooks/use-container-tags"
import { useHasCompanyBrain } from "@/hooks/use-company-brain"
import { PopoverAnchor } from "@ui/components/popover"
import { OrgContext } from "@/components/settings/org-context"
import { WorkspacePersona } from "@/components/settings/workspace-persona"
import { WorkspacePrompt } from "@/components/settings/workspace-prompt"
import { OrgPlanBadge } from "@/components/org-plan-badge"
import { useTokenUsage } from "@/hooks/use-token-usage"
import { useOrgSummaries } from "@/hooks/use-org-summaries"
@ -623,7 +623,7 @@ export default function Account() {
{isCompanyBrain && (
<>
<div aria-hidden="true" className="mx-1 h-px bg-white/[0.055]" />
<WorkspacePersona key={org?.id} />
<WorkspacePrompt key={org?.id} />
</>
)}
</div>

View file

@ -7,9 +7,9 @@ import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts"
import { cn } from "@lib/utils"
import { useAuth } from "@lib/auth-context"
const DESCRIPTION_ID = "workspace-persona-description"
const COUNTER_ID = "workspace-persona-counter"
const HEADING_ID = "workspace-persona-heading"
const DESCRIPTION_ID = "workspace-prompt-description"
const COUNTER_ID = "workspace-prompt-counter"
const HEADING_ID = "workspace-prompt-heading"
function SectionHeading({ children }: { children: React.ReactNode }) {
return (
@ -25,10 +25,10 @@ function SectionHeading({ children }: { children: React.ReactNode }) {
)
}
function PersonaHeader() {
function PromptHeader() {
return (
<div className="min-w-0">
<SectionHeading>Workspace Persona</SectionHeading>
<SectionHeading>Workspace Prompt</SectionHeading>
<p
id={DESCRIPTION_ID}
className={cn(
@ -36,49 +36,52 @@ function PersonaHeader() {
"text-[13px] tracking-[-0.13px] text-[#737373]",
)}
>
Guide how Company Brain communicates and works with your workspace.
Set persistent guidance for operating preferences, priorities, source
and tool choices, workflows, terminology, formatting, and communication
style. This guidance cannot override fixed safety, access, or approval
constraints.
</p>
</div>
)
}
export function WorkspacePersona() {
export function WorkspacePrompt() {
const { org } = useAuth()
const settingsQuery = useOrgSettings()
const updateSettings = useUpdateOrgSettings()
const [persona, setPersona] = useState("")
const syncedPersona = useRef<{ orgId: string; value: string } | null>(null)
const [prompt, setPrompt] = useState("")
const syncedPrompt = useRef<{ orgId: string; value: string } | null>(null)
const savedPersona = settingsQuery.data?.workspacePersona ?? ""
const dirty = persona.trim() !== savedPersona.trim()
const savedPrompt = settingsQuery.data?.workspacePrompt ?? ""
const dirty = prompt.trim() !== savedPrompt.trim()
useEffect(() => {
if (!settingsQuery.data) return
const nextPersona = settingsQuery.data.workspacePersona ?? ""
const nextPrompt = settingsQuery.data.workspacePrompt ?? ""
const orgId = org?.id ?? ""
const previous = syncedPersona.current
const previous = syncedPrompt.current
setPersona((current) => {
if (!previous || previous.orgId !== orgId) return nextPersona
return current === previous.value ? nextPersona : current
setPrompt((current) => {
if (!previous || previous.orgId !== orgId) return nextPrompt
return current === previous.value ? nextPrompt : current
})
syncedPersona.current = { orgId, value: nextPersona }
syncedPrompt.current = { orgId, value: nextPrompt }
}, [org?.id, settingsQuery.data])
const handleSave = () => {
updateSettings.mutate({
workspacePersona: persona.trim() ? persona.trim() : null,
workspacePrompt: prompt.trim() ? prompt.trim() : null,
})
}
return (
<section
id="workspace-persona"
id="workspace-prompt"
aria-labelledby={HEADING_ID}
className="flex flex-col gap-3 px-1"
>
<PersonaHeader />
<PromptHeader />
{settingsQuery.isLoading ? (
<div
@ -88,14 +91,14 @@ export function WorkspacePersona() {
)}
>
<LoaderIcon className="size-3 animate-spin" />
Loading workspace persona…
Loading workspace prompt…
</div>
) : settingsQuery.isError ? (
<div
className={cn(dmSansClassName(), "flex flex-col items-start gap-2")}
>
<p className="text-[13px] text-[#A3A3A3]">
Workspace persona could not be loaded.
Workspace prompt could not be loaded.
</p>
<button
type="button"
@ -114,9 +117,9 @@ export function WorkspacePersona() {
<textarea
aria-labelledby={HEADING_ID}
aria-describedby={`${DESCRIPTION_ID} ${COUNTER_ID}`}
value={persona}
onChange={(event) => setPersona(event.target.value)}
placeholder="Describe your workspace's goals, communication style, and how you prefer Company Brain to collaborate with your team..."
value={prompt}
onChange={(event) => setPrompt(event.target.value)}
placeholder="Add persistent guidance for how Company Brain should operate across your workspace..."
maxLength={1500}
className="min-h-[96px] w-full resize-y rounded-[12px] border border-white/[0.08] bg-[#0D121A] px-3.5 py-3 text-[13px] leading-relaxed text-[#FAFAFA] placeholder:text-[#525966] focus:border-white/[0.16] focus:outline-none"
/>
@ -125,13 +128,13 @@ export function WorkspacePersona() {
id={COUNTER_ID}
className="text-[11px] text-[#737373] tabular-nums"
>
{persona.length}/1500
{prompt.length}/1500
</span>
{dirty && (
<div className="flex items-center gap-2">
<button
type="button"
onClick={() => setPersona(savedPersona)}
onClick={() => setPrompt(savedPrompt)}
disabled={updateSettings.isPending}
className={cn(
dmSansClassName(),

View file

@ -6,7 +6,7 @@ import { useAuth } from "@lib/auth-context"
export type OrgSettings = {
shouldLLMFilter: boolean
filterPrompt: string | null
workspacePersona: string | null
workspacePrompt: string | null
includeItems?: string[] | null
excludeItems?: string[] | null
}
@ -33,7 +33,7 @@ export function useOrgSettings() {
return {
shouldLLMFilter: settings.shouldLLMFilter ?? false,
filterPrompt: settings.filterPrompt ?? null,
workspacePersona: settings.workspacePersona ?? null,
workspacePrompt: settings.workspacePrompt ?? null,
includeItems: settings.includeItems ?? null,
excludeItems: settings.excludeItems ?? null,
}

View file

@ -37,7 +37,7 @@ const SettingsResponseSchema = z.object({
filterPrompt: z.string().max(750).nullable().optional(),
includeItems: z.array(z.string().min(1).max(20)).nullable().optional(),
shouldLLMFilter: z.boolean().optional(),
workspacePersona: z.string().max(1500).nullable().optional(),
workspacePrompt: z.string().max(1500).nullable().optional(),
}),
})

View file

@ -315,7 +315,7 @@ export const OrganizationSettingsSchema = z.object({
filterPrompt: z.string().nullable().optional(),
includeItems: z.array(z.string()).nullable().optional(),
excludeItems: z.array(z.string()).nullable().optional(),
workspacePersona: z.string().max(1500).nullable().optional(),
workspacePrompt: z.string().max(1500).nullable().optional(),
// Google Drive custom keys
googleDriveCustomKeyEnabled: z.boolean().default(false),