From 941d38a2fd6314dfc1492cac62e71f69c9615044 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 2 Jul 2026 17:13:35 +0530 Subject: [PATCH 1/3] feat(persona): guided persona builder over SOUL.md (#4253) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a structured, non-technical persona editor that maps friendly fields (Personality, Communication style, About you) to named SOUL.md sections and splices them in place, keeping SOUL.md the runtime source of truth. Guided is the default; the raw markdown editor stays behind an Advanced toggle. Reuses the existing workspace_file_read/write/reset RPC — no core changes. Part of the phased #4253 work (PR1 of N). --- .../settings/panels/PersonaPanel.test.tsx | 78 ++++++++--- .../settings/panels/PersonaPanel.tsx | 53 +++++-- .../panels/persona/PersonaGuidedFields.tsx | 98 +++++++++++++ .../panels/persona/personaSections.test.ts | 91 ++++++++++++ .../panels/persona/personaSections.ts | 129 ++++++++++++++++++ app/src/lib/i18n/ar.ts | 13 ++ app/src/lib/i18n/bn.ts | 13 ++ app/src/lib/i18n/de.ts | 13 ++ app/src/lib/i18n/en.ts | 18 +++ app/src/lib/i18n/es.ts | 13 ++ app/src/lib/i18n/fr.ts | 13 ++ app/src/lib/i18n/hi.ts | 13 ++ app/src/lib/i18n/id.ts | 13 ++ app/src/lib/i18n/it.ts | 13 ++ app/src/lib/i18n/ko.ts | 13 ++ app/src/lib/i18n/pl.ts | 13 ++ app/src/lib/i18n/pt.ts | 13 ++ app/src/lib/i18n/ru.ts | 13 ++ app/src/lib/i18n/zh-CN.ts | 13 ++ 19 files changed, 609 insertions(+), 27 deletions(-) create mode 100644 app/src/components/settings/panels/persona/PersonaGuidedFields.tsx create mode 100644 app/src/components/settings/panels/persona/personaSections.test.ts create mode 100644 app/src/components/settings/panels/persona/personaSections.ts diff --git a/app/src/components/settings/panels/PersonaPanel.test.tsx b/app/src/components/settings/panels/PersonaPanel.test.tsx index 3f8661142d..d32b0c81a1 100644 --- a/app/src/components/settings/panels/PersonaPanel.test.tsx +++ b/app/src/components/settings/panels/PersonaPanel.test.tsx @@ -40,6 +40,13 @@ const soulFile = (overrides: Record = {}) => ({ ...overrides, }); +/** Wait for the SOUL section to finish loading (the mode toggle is always shown). */ +const awaitLoaded = () => + waitFor(() => expect(screen.getByTestId('persona-soul-mode-guided')).toBeInTheDocument()); + +/** Switch to the Advanced (raw markdown) editor. */ +const openAdvanced = () => fireEvent.click(screen.getByTestId('persona-soul-mode-advanced')); + describe('PersonaPanel', () => { beforeEach(() => { vi.clearAllMocks(); @@ -47,22 +54,50 @@ describe('PersonaPanel', () => { writePersonaFileMock.mockImplementation((_name: string, contents: string) => Promise.resolve(soulFile({ contents, is_default: false })) ); - resetPersonaFileMock.mockResolvedValue( - soulFile({ contents: 'default soul', is_default: true }) - ); + resetPersonaFileMock.mockResolvedValue(soulFile({ contents: 'default soul', is_default: true })); + }); + + it('defaults to the guided builder and hides raw markdown', async () => { + renderWithProviders(); + await awaitLoaded(); + expect(screen.getByTestId('persona-guided-personality')).toBeInTheDocument(); + expect(screen.queryByTestId('persona-soul-editor')).not.toBeInTheDocument(); + expect(readPersonaFileMock).toHaveBeenCalledWith('SOUL.md'); + }); + + it('reveals the raw SOUL.md editor in Advanced mode', async () => { + renderWithProviders(); + await awaitLoaded(); + openAdvanced(); + expect(screen.getByTestId('persona-soul-editor')).toHaveValue('You are helpful.'); }); - it('loads SOUL.md contents into the editor on mount', async () => { + it('splices a guided field edit into SOUL.md and saves it over RPC', async () => { + readPersonaFileMock.mockResolvedValue( + soulFile({ contents: '## Personality\n\nOld.\n', is_default: false }) + ); renderWithProviders(); + await awaitLoaded(); + + await waitFor(() => + expect(screen.getByTestId('persona-guided-personality')).toHaveValue('Old.') + ); + fireEvent.change(screen.getByTestId('persona-guided-personality'), { + target: { value: 'Warm and direct.' }, + }); + fireEvent.click(screen.getByTestId('persona-soul-save')); + await waitFor(() => { - expect(screen.getByTestId('persona-soul-editor')).toHaveValue('You are helpful.'); + expect(writePersonaFileMock).toHaveBeenCalledWith( + 'SOUL.md', + '## Personality\n\nWarm and direct.\n' + ); }); - expect(readPersonaFileMock).toHaveBeenCalledWith('SOUL.md'); }); it('persists the display name to the store on save', async () => { const { store } = renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toBeInTheDocument()); + await awaitLoaded(); fireEvent.change(screen.getByTestId('persona-display-name-input'), { target: { value: 'Nova' }, @@ -78,13 +113,14 @@ describe('PersonaPanel', () => { it('keeps the identity save button disabled until a field changes', async () => { renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toBeInTheDocument()); + await awaitLoaded(); expect(screen.getByTestId('persona-identity-save')).toBeDisabled(); }); - it('writes edited SOUL.md contents over RPC', async () => { + it('writes edited SOUL.md contents over RPC from the raw editor', async () => { renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toBeInTheDocument()); + await awaitLoaded(); + openAdvanced(); fireEvent.change(screen.getByTestId('persona-soul-editor'), { target: { value: 'You are calm and concise.' }, @@ -99,7 +135,8 @@ describe('PersonaPanel', () => { it('surfaces a save error when the write RPC fails', async () => { writePersonaFileMock.mockRejectedValue(new Error('disk full')); renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toBeInTheDocument()); + await awaitLoaded(); + openAdvanced(); fireEvent.change(screen.getByTestId('persona-soul-editor'), { target: { value: 'edited' } }); fireEvent.click(screen.getByTestId('persona-soul-save')); @@ -113,7 +150,7 @@ describe('PersonaPanel', () => { readPersonaFileMock.mockResolvedValue(soulFile({ contents: 'custom', is_default: false })); resetPersonaFileMock.mockRejectedValue(new Error('reset boom')); renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toHaveValue('custom')); + await awaitLoaded(); fireEvent.click(screen.getByTestId('persona-soul-reset')); @@ -126,9 +163,9 @@ describe('PersonaPanel', () => { // Start from a non-default file so the Reset button is enabled. readPersonaFileMock.mockResolvedValue(soulFile({ contents: 'custom', is_default: false })); renderWithProviders(); - await waitFor(() => { - expect(screen.getByTestId('persona-soul-editor')).toHaveValue('custom'); - }); + await awaitLoaded(); + openAdvanced(); + await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toHaveValue('custom')); fireEvent.click(screen.getByTestId('persona-soul-reset')); @@ -140,7 +177,7 @@ describe('PersonaPanel', () => { it('disables Reset while the file is already the bundled default', async () => { renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toBeInTheDocument()); + await awaitLoaded(); expect(screen.getByTestId('persona-soul-reset')).toBeDisabled(); expect(screen.getByTestId('persona-soul-default-badge')).toBeInTheDocument(); }); @@ -155,8 +192,15 @@ describe('PersonaPanel', () => { it('navigates to the Face tab for avatar & voice', async () => { renderWithProviders(); - await waitFor(() => expect(screen.getByTestId('persona-soul-editor')).toBeInTheDocument()); + await awaitLoaded(); fireEvent.click(screen.getByTestId('persona-open-mascot')); expect(mockNavigateToSettings).toHaveBeenCalledWith('personality#face'); }); + + it('links guided users to Agent access for permissions', async () => { + renderWithProviders(); + await awaitLoaded(); + fireEvent.click(screen.getByTestId('persona-guided-agent-access')); + expect(mockNavigateToSettings).toHaveBeenCalledWith('agent-access'); + }); }); diff --git a/app/src/components/settings/panels/PersonaPanel.tsx b/app/src/components/settings/panels/PersonaPanel.tsx index c406bb0bab..4843aae7fa 100644 --- a/app/src/components/settings/panels/PersonaPanel.tsx +++ b/app/src/components/settings/panels/PersonaPanel.tsx @@ -21,6 +21,9 @@ import Button from '../../ui/Button'; import { SettingsRow, SettingsSection, SettingsTextArea, SettingsTextField } from '../controls'; import { useSettingsNavigation } from '../hooks/useSettingsNavigation'; import SettingsPanel from '../layout/SettingsPanel'; +import PersonaGuidedFields from './persona/PersonaGuidedFields'; + +type SoulMode = 'guided' | 'advanced'; const log = debug('persona:panel'); @@ -59,6 +62,9 @@ const PersonaPanel = ({ embedded = false }: PersonaPanelProps) => { const [soulLoading, setSoulLoading] = useState(true); const [soulError, setSoulError] = useState(null); const [soulBusy, setSoulBusy] = useState(false); + // Guided (structured fields) is the default so users never touch raw markdown; + // Advanced exposes the full SOUL.md text editor for power users. + const [soulMode, setSoulMode] = useState('guided'); useEffect(() => { let cancelled = false; @@ -195,17 +201,44 @@ const PersonaPanel = ({ embedded = false }: PersonaPanelProps) => { ) : ( <> -
- setSoulDraft(e.target.value)} - /> +
+ +
+ {soulMode === 'guided' ? ( + + ) : ( +
+ setSoulDraft(e.target.value)} + /> +
+ )}
+

+
+ ); +}; + +export default PersonaGuidedFields; diff --git a/app/src/components/settings/panels/persona/personaSections.test.ts b/app/src/components/settings/panels/persona/personaSections.test.ts new file mode 100644 index 0000000000..d6238bb703 --- /dev/null +++ b/app/src/components/settings/panels/persona/personaSections.test.ts @@ -0,0 +1,91 @@ +import { describe, expect, it } from 'vitest'; + +import { + applyPersonaField, + applyPersonaFields, + parsePersonaFields, +} from './personaSections'; + +const SOUL = `# OpenHuman + +You are OpenHuman. + +## Personality + +- Warm +- Direct + +## Voice + +- Lead with the answer. + +## When things go wrong + +- Own it. +`; + +describe('parsePersonaFields', () => { + it('reads the managed sections and leaves About empty when absent', () => { + const fields = parsePersonaFields(SOUL); + expect(fields.personality).toBe('- Warm\n- Direct'); + expect(fields.voice).toBe('- Lead with the answer.'); + expect(fields.about).toBe(''); + }); + + it('does not match a deeper or differently-named heading', () => { + const text = '## Personality Traits\n\nfoo\n\n### Personality\n\nbar\n'; + expect(parsePersonaFields(text).personality).toBe(''); + }); + + it('includes nested h3 content but stops at the next h2', () => { + const text = '## Personality\n\n- a\n### sub\n- b\n\n## Voice\n\nx\n'; + expect(parsePersonaFields(text).personality).toBe('- a\n### sub\n- b'); + expect(parsePersonaFields(text).voice).toBe('x'); + }); +}); + +describe('applyPersonaField', () => { + it('is a no-op (identical string) when the value is unchanged', () => { + expect(applyPersonaField(SOUL, 'personality', '- Warm\n- Direct')).toBe(SOUL); + // trimming differences also count as unchanged + expect(applyPersonaField(SOUL, 'voice', ' - Lead with the answer. ')).toBe(SOUL); + }); + + it('replaces only the target section and preserves every other byte', () => { + const next = applyPersonaField(SOUL, 'voice', 'Be terse.'); + expect(parsePersonaFields(next).voice).toBe('Be terse.'); + // untouched sections are byte-identical + expect(next).toContain('## Personality\n\n- Warm\n- Direct'); + expect(next).toContain('## When things go wrong\n\n- Own it.'); + // and re-applying the original value restores the exact original document + expect(applyPersonaField(next, 'voice', '- Lead with the answer.')).toBe(SOUL); + }); + + it('appends a new section when the managed heading is absent', () => { + const next = applyPersonaField(SOUL, 'about', 'I design things.'); + expect(next.startsWith(SOUL.replace(/\n*$/, '\n'))).toBe(true); + expect(next).toContain('## About You\n\nI design things.\n'); + expect(parsePersonaFields(next).about).toBe('I design things.'); + }); + + it('empties the body but keeps the heading when cleared', () => { + const next = applyPersonaField(SOUL, 'voice', ''); + expect(parsePersonaFields(next).voice).toBe(''); + expect(next).toContain('## Voice'); + expect(next).toContain('## When things go wrong'); + }); +}); + +describe('applyPersonaFields round-trip', () => { + it('is idempotent when nothing changed', () => { + expect(applyPersonaFields(SOUL, parsePersonaFields(SOUL))).toBe(SOUL); + }); + + it('round-trips edited fields through parse → apply', () => { + const edited = { personality: 'Calm.', voice: 'Brief.', about: 'A designer.' }; + const next = applyPersonaFields(SOUL, edited); + expect(parsePersonaFields(next)).toEqual(edited); + // applying the parsed fields again changes nothing further + expect(applyPersonaFields(next, parsePersonaFields(next))).toBe(next); + }); +}); diff --git a/app/src/components/settings/panels/persona/personaSections.ts b/app/src/components/settings/panels/persona/personaSections.ts new file mode 100644 index 0000000000..dae3c7fa6c --- /dev/null +++ b/app/src/components/settings/panels/persona/personaSections.ts @@ -0,0 +1,129 @@ +/** + * SOUL.md ⇄ structured-persona round-trip (issue #4253, PR1). + * + * The guided persona builder edits a handful of named markdown sections inside + * `SOUL.md` without asking the user to write markdown. `SOUL.md` stays the + * single source of truth the assistant runtime reads — we only splice the + * managed sections in place and leave every other byte (the title, the intro, + * and any hand-written sections) untouched. That keeps the round-trip lossless + * and idempotent: parsing then re-applying an unchanged value returns the exact + * same string. + */ + +/** Managed field keys the guided builder can edit. */ +export type PersonaFieldKey = 'personality' | 'voice' | 'about'; + +export interface PersonaSectionDef { + key: PersonaFieldKey; + /** Canonical `## ` heading text this field maps to inside SOUL.md. */ + heading: string; +} + +/** + * The sections the guided builder owns. `Personality` and `Voice` already ship + * in the bundled SOUL.md; `About You` is created on demand the first time the + * user fills it in. Anything not listed here is preserved verbatim. + */ +export const PERSONA_SECTIONS: readonly PersonaSectionDef[] = [ + { key: 'personality', heading: 'Personality' }, + { key: 'voice', heading: 'Voice' }, + { key: 'about', heading: 'About You' }, +] as const; + +export type PersonaFields = Record; + +const HEADING_FOR: Record = { + personality: 'Personality', + voice: 'Voice', + about: 'About You', +}; + +interface SectionSpan { + /** First char of the body (after the heading line's newline). */ + bodyStart: number; + /** One past the last char of the body (start of the next `#`/`##` heading, or EOF). */ + bodyEnd: number; +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Locate a `## ` block and return the char range of its body. Matches + * are case-insensitive and require the heading to be the entire line, so + * `## Personality` matches but `## Personality Traits` and `### Personality` do + * not. The body runs until the next level-1 or level-2 ATX heading (deeper + * `###` headings stay part of the body), or end-of-string. + */ +function findSectionSpan(text: string, heading: string): SectionSpan | null { + const headingRe = new RegExp(`^##[ \\t]+${escapeRegExp(heading)}[ \\t]*$`, 'im'); + const match = headingRe.exec(text); + if (!match) return null; + + const newlineIdx = text.indexOf('\n', match.index); + const bodyStart = newlineIdx === -1 ? text.length : newlineIdx + 1; + + const nextHeadingRe = /^#{1,2}[ \t]/m; + const rest = text.slice(bodyStart); + const nextMatch = nextHeadingRe.exec(rest); + const bodyEnd = nextMatch ? bodyStart + nextMatch.index : text.length; + + return { bodyStart, bodyEnd }; +} + +/** Read the trimmed body of a managed section, or `''` if it is absent. */ +function readSection(text: string, heading: string): string { + const span = findSectionSpan(text, heading); + if (!span) return ''; + return text.slice(span.bodyStart, span.bodyEnd).trim(); +} + +/** Parse the managed persona fields out of a SOUL.md document. */ +export function parsePersonaFields(soul: string): PersonaFields { + return { + personality: readSection(soul, HEADING_FOR.personality), + voice: readSection(soul, HEADING_FOR.voice), + about: readSection(soul, HEADING_FOR.about), + }; +} + +/** + * Return a copy of `soul` with a single managed field set to `value`, splicing + * only that section and leaving the rest of the document byte-for-byte intact. + * + * - If the value is unchanged, the original string is returned unchanged. + * - If the section exists, its inner content is replaced while the surrounding + * blank lines are preserved (clean seams, stable diffs). + * - If the section is absent and the value is non-empty, a new `## ` + * block is appended after a single trailing newline. + * - Clearing an existing section empties its body but keeps the heading. + */ +export function applyPersonaField(soul: string, key: PersonaFieldKey, value: string): string { + const heading = HEADING_FOR[key]; + const nextBody = value.trim(); + + if (readSection(soul, heading) === nextBody) return soul; + + const span = findSectionSpan(soul, heading); + if (span) { + const raw = soul.slice(span.bodyStart, span.bodyEnd); + const lead = raw.match(/^\n*/)?.[0] ?? ''; + const trail = raw.match(/\n*$/)?.[0] ?? ''; + const spliced = nextBody ? `${lead}${nextBody}${trail || '\n'}` : `${lead}${trail}`; + return soul.slice(0, span.bodyStart) + spliced + soul.slice(span.bodyEnd); + } + + if (!nextBody) return soul; + const base = soul.replace(/\n*$/, '\n'); + return `${base}\n## ${heading}\n\n${nextBody}\n`; +} + +/** Apply every managed field at once (used for save-all / tests). */ +export function applyPersonaFields(soul: string, fields: PersonaFields): string { + let next = soul; + for (const { key } of PERSONA_SECTIONS) { + next = applyPersonaField(next, key, fields[key]); + } + return next; +} diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts index 22ca4d8c42..a2f31478a3 100644 --- a/app/src/lib/i18n/ar.ts +++ b/app/src/lib/i18n/ar.ts @@ -4867,6 +4867,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'لا يمكن تحميل Xqx0xx', 'settings.persona.soul.saveError': 'لا يُمكنُ أَنْ يَوفّرَ Xqx0xxx', 'settings.persona.soul.resetError': 'لا يمكن إعادة تشغيل Xqx0xx', + 'settings.persona.builder.modeLabel': 'وضع محرر الشخصية', + 'settings.persona.builder.modeGuided': 'موجّه', + 'settings.persona.builder.modeAdvanced': 'متقدّم', + 'settings.persona.builder.intro': 'املأ بضعة حقول وسنكتبها في شخصيتك نيابةً عنك. لا حاجة إلى ماركداون.', + 'settings.persona.builder.personalityLabel': 'الشخصية', + 'settings.persona.builder.personalityPlaceholder': 'مثال: ودود وفضولي ومباشر. صادق عند عدم اليقين.', + 'settings.persona.builder.voiceLabel': 'أسلوب التواصل', + 'settings.persona.builder.voicePlaceholder': 'مثال: ابدأ بالإجابة، واجعلها موجزة، وطابق نبرتي.', + 'settings.persona.builder.aboutLabel': 'نبذة عنك', + 'settings.persona.builder.aboutPlaceholder': 'مثال: أدير استوديو تصميم صغيرًا وأفضّل اللغة البسيطة.', + 'settings.persona.builder.preservedNote': 'تُحفظ أي أقسام أخرى كتبتها بنفسك — انتقل إلى «متقدّم» لرؤية الشخصية كاملة.', + 'settings.persona.builder.securityNote': 'هل تريد تحديد ما يُسمح للمساعد بفعله؟', + 'settings.persona.builder.securityLink': 'فتح وصول الوكيل', 'settings.persona.appearanceHeading': 'صوت الأفاتار', 'settings.persona.appearanceDesc': 'لون الماسكوت، العرف Xqx0x avatar، وصوت الرد مصمم في أماكن مكوت.', diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts index 7265f80cab..3033547adf 100644 --- a/app/src/lib/i18n/bn.ts +++ b/app/src/lib/i18n/bn.ts @@ -4967,6 +4967,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'xqxqx লোড করতে ব্যর্থ', 'settings.persona.soul.saveError': 'ছবি সংরক্ষণ করতে ব্যর্থx% 1', 'settings.persona.soul.resetError': 'xqxqx সার্ভার আরম্ভ করতে ব্যর্থ', + 'settings.persona.builder.modeLabel': 'পারসোনা এডিটর মোড', + 'settings.persona.builder.modeGuided': 'গাইডেড', + 'settings.persona.builder.modeAdvanced': 'অ্যাডভান্সড', + 'settings.persona.builder.intro': 'কয়েকটি ঘর পূরণ করুন, আমরা সেগুলো আপনার পারসোনায় লিখে দেব। মার্কডাউন লাগবে না।', + 'settings.persona.builder.personalityLabel': 'ব্যক্তিত্ব', + 'settings.persona.builder.personalityPlaceholder': 'যেমন: উষ্ণ, কৌতূহলী ও স্পষ্টবাদী। অনিশ্চয়তায় সৎ।', + 'settings.persona.builder.voiceLabel': 'যোগাযোগের ধরন', + 'settings.persona.builder.voicePlaceholder': 'যেমন: আগে উত্তর দিন, সংক্ষিপ্ত রাখুন এবং আমার সুরে মিলিয়ে নিন।', + 'settings.persona.builder.aboutLabel': 'আপনার সম্পর্কে', + 'settings.persona.builder.aboutPlaceholder': 'যেমন: আমি একটি ছোট ডিজাইন স্টুডিও চালাই এবং সহজ ভাষা পছন্দ করি।', + 'settings.persona.builder.preservedNote': 'আপনার নিজের হাতে লেখা অন্যান্য অংশ সংরক্ষিত থাকে — সম্পূর্ণ পারসোনা দেখতে অ্যাডভান্সড-এ যান।', + 'settings.persona.builder.securityNote': 'সহকারী কী করতে পারবে তা ঠিক করতে চান?', + 'settings.persona.builder.securityLink': 'এজেন্ট অ্যাক্সেস খুলুন', 'settings.persona.appearanceHeading': 'অবতার & ভয়েস', 'settings.persona.appearanceDesc': 'Mascot রঙের রং, স্বনির্ধারিত xxqxqx অ্যাভাটার, এবং Scotox বৈশিষ্ট্যের মধ্য থেকে ভয়েস কনফিগার করা হয়েছে।', diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts index 7552c1c1ca..aa1dd76b32 100644 --- a/app/src/lib/i18n/de.ts +++ b/app/src/lib/i18n/de.ts @@ -5096,6 +5096,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'SOUL.md konnte nicht geladen werden', 'settings.persona.soul.saveError': 'SOUL.md konnte nicht gespeichert werden', 'settings.persona.soul.resetError': 'SOUL.md konnte nicht zurückgesetzt werden', + 'settings.persona.builder.modeLabel': 'Persona-Editor-Modus', + 'settings.persona.builder.modeGuided': 'Geführt', + 'settings.persona.builder.modeAdvanced': 'Erweitert', + 'settings.persona.builder.intro': 'Füllen Sie ein paar Felder aus und wir schreiben sie für Sie in Ihre Persona. Kein Markdown nötig.', + 'settings.persona.builder.personalityLabel': 'Persönlichkeit', + 'settings.persona.builder.personalityPlaceholder': 'z. B. Warmherzig, neugierig und direkt. Ehrlich bei Unsicherheit.', + 'settings.persona.builder.voiceLabel': 'Kommunikationsstil', + 'settings.persona.builder.voicePlaceholder': 'z. B. Zuerst die Antwort, kurz halten und meinen Ton treffen.', + 'settings.persona.builder.aboutLabel': 'Über Sie', + 'settings.persona.builder.aboutPlaceholder': 'z. B. Ich leite ein kleines Designstudio und bevorzuge klare Sprache.', + 'settings.persona.builder.preservedNote': 'Alle anderen von Hand geschriebenen Abschnitte bleiben erhalten — wechseln Sie zu „Erweitert“, um die vollständige Persona zu sehen.', + 'settings.persona.builder.securityNote': 'Möchten Sie festlegen, was der Assistent tun darf?', + 'settings.persona.builder.securityLink': 'Agentenzugriff öffnen', 'settings.persona.appearanceHeading': 'Avatar und Stimme', 'settings.persona.appearanceDesc': 'Maskottchenfarbe, benutzerdefinierter GIF-Avatar und Antwortstimme werden in den Maskottcheneinstellungen konfiguriert.', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index 82914a54af..11bf16309c 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -5607,6 +5607,24 @@ const en: TranslationMap = { 'settings.persona.soul.loadError': 'Could not load SOUL.md', 'settings.persona.soul.saveError': 'Could not save SOUL.md', 'settings.persona.soul.resetError': 'Could not reset SOUL.md', + 'settings.persona.builder.modeLabel': 'Persona editor mode', + 'settings.persona.builder.modeGuided': 'Guided', + 'settings.persona.builder.modeAdvanced': 'Advanced', + 'settings.persona.builder.intro': + 'Fill in a few fields and we write them into your persona for you. No markdown required.', + 'settings.persona.builder.personalityLabel': 'Personality', + 'settings.persona.builder.personalityPlaceholder': + 'e.g. Warm, curious, and direct. Honest about uncertainty.', + 'settings.persona.builder.voiceLabel': 'Communication style', + 'settings.persona.builder.voicePlaceholder': + 'e.g. Lead with the answer, keep it brief, and match my tone.', + 'settings.persona.builder.aboutLabel': 'About you', + 'settings.persona.builder.aboutPlaceholder': + 'e.g. I run a small design studio and prefer plain language.', + 'settings.persona.builder.preservedNote': + 'Any other sections you wrote by hand are kept — switch to Advanced to see the full persona.', + 'settings.persona.builder.securityNote': 'Choosing what the assistant is allowed to do?', + 'settings.persona.builder.securityLink': 'Open Agent access', 'settings.persona.appearanceHeading': 'Avatar & Voice', 'settings.persona.appearanceDesc': 'Mascot color, custom GIF avatar, and reply voice are configured in Mascot settings.', diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts index cf8bfc7c79..6139a02217 100644 --- a/app/src/lib/i18n/es.ts +++ b/app/src/lib/i18n/es.ts @@ -5061,6 +5061,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'No se pudo cargar SOUL.md', 'settings.persona.soul.saveError': 'No se pudo guardar SOUL.md', 'settings.persona.soul.resetError': 'No se pudo restablecer SOUL.md', + 'settings.persona.builder.modeLabel': 'Modo del editor de persona', + 'settings.persona.builder.modeGuided': 'Guiado', + 'settings.persona.builder.modeAdvanced': 'Avanzado', + 'settings.persona.builder.intro': 'Rellena unos campos y los escribimos en tu persona por ti. No hace falta markdown.', + 'settings.persona.builder.personalityLabel': 'Personalidad', + 'settings.persona.builder.personalityPlaceholder': 'p. ej. Cercano, curioso y directo. Honesto ante la incertidumbre.', + 'settings.persona.builder.voiceLabel': 'Estilo de comunicación', + 'settings.persona.builder.voicePlaceholder': 'p. ej. Empieza por la respuesta, sé breve y adapta mi tono.', + 'settings.persona.builder.aboutLabel': 'Sobre ti', + 'settings.persona.builder.aboutPlaceholder': 'p. ej. Dirijo un pequeño estudio de diseño y prefiero un lenguaje sencillo.', + 'settings.persona.builder.preservedNote': 'Cualquier otra sección que hayas escrito a mano se conserva: cambia a Avanzado para ver la persona completa.', + 'settings.persona.builder.securityNote': '¿Quieres elegir lo que el asistente puede hacer?', + 'settings.persona.builder.securityLink': 'Abrir Acceso del agente', 'settings.persona.appearanceHeading': 'Avatar y Voz', 'settings.persona.appearanceDesc': 'El color de la mascota, el avatar personalizado GIF y la voz de respuesta se configuran en los ajustes de la mascota.', diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts index 4292d98952..f0ca71d230 100644 --- a/app/src/lib/i18n/fr.ts +++ b/app/src/lib/i18n/fr.ts @@ -5080,6 +5080,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'Impossible de charger SOUL.md', 'settings.persona.soul.saveError': "Impossible d'enregistrer SOUL.md", 'settings.persona.soul.resetError': 'Impossible de réinitialiser SOUL.md', + 'settings.persona.builder.modeLabel': 'Mode de l’éditeur de persona', + 'settings.persona.builder.modeGuided': 'Guidé', + 'settings.persona.builder.modeAdvanced': 'Avancé', + 'settings.persona.builder.intro': 'Remplissez quelques champs et nous les écrivons dans votre persona. Aucun markdown requis.', + 'settings.persona.builder.personalityLabel': 'Personnalité', + 'settings.persona.builder.personalityPlaceholder': 'p. ex. Chaleureux, curieux et direct. Honnête face à l’incertitude.', + 'settings.persona.builder.voiceLabel': 'Style de communication', + 'settings.persona.builder.voicePlaceholder': 'p. ex. Commencez par la réponse, restez bref et adaptez mon ton.', + 'settings.persona.builder.aboutLabel': 'À propos de vous', + 'settings.persona.builder.aboutPlaceholder': 'p. ex. Je dirige un petit studio de design et je préfère un langage simple.', + 'settings.persona.builder.preservedNote': 'Toutes les autres sections que vous avez écrites à la main sont conservées — passez en mode Avancé pour voir la persona complète.', + 'settings.persona.builder.securityNote': 'Vous voulez choisir ce que l’assistant est autorisé à faire ?', + 'settings.persona.builder.securityLink': 'Ouvrir Accès de l’agent', 'settings.persona.appearanceHeading': 'Avatar et Voix', 'settings.persona.appearanceDesc': "La couleur de la mascotte, l'avatar personnalisé GIF et la voix de réponse sont configurés dans les paramètres de la mascotte.", diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts index 3243bd11bc..592d99d3f2 100644 --- a/app/src/lib/i18n/hi.ts +++ b/app/src/lib/i18n/hi.ts @@ -4971,6 +4971,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'SOUL', 'settings.persona.soul.saveError': 'नहीं बचा सकता SOUL.md', 'settings.persona.soul.resetError': 'SOUL.md रीसेट नहीं कर सका', + 'settings.persona.builder.modeLabel': 'पर्सोना एडिटर मोड', + 'settings.persona.builder.modeGuided': 'निर्देशित', + 'settings.persona.builder.modeAdvanced': 'उन्नत', + 'settings.persona.builder.intro': 'कुछ फ़ील्ड भरें और हम उन्हें आपके पर्सोना में लिख देंगे। मार्कडाउन की ज़रूरत नहीं।', + 'settings.persona.builder.personalityLabel': 'व्यक्तित्व', + 'settings.persona.builder.personalityPlaceholder': 'जैसे: गर्मजोश, जिज्ञासु और सीधा। अनिश्चितता पर ईमानदार।', + 'settings.persona.builder.voiceLabel': 'संवाद शैली', + 'settings.persona.builder.voicePlaceholder': 'जैसे: पहले उत्तर दें, संक्षिप्त रखें और मेरे लहजे से मेल खाएँ।', + 'settings.persona.builder.aboutLabel': 'आपके बारे में', + 'settings.persona.builder.aboutPlaceholder': 'जैसे: मैं एक छोटा डिज़ाइन स्टूडियो चलाता हूँ और सरल भाषा पसंद करता हूँ।', + 'settings.persona.builder.preservedNote': 'आपके हाथ से लिखे अन्य अनुभाग सुरक्षित रहते हैं — पूरा पर्सोना देखने के लिए उन्नत पर जाएँ।', + 'settings.persona.builder.securityNote': 'तय करना चाहते हैं कि असिस्टेंट क्या कर सकता है?', + 'settings.persona.builder.securityLink': 'एजेंट एक्सेस खोलें', 'settings.persona.appearanceHeading': 'अवतार और आवाज', 'settings.persona.appearanceDesc': 'Mascot रंग, कस्टम GIF अवतार, और उत्तर आवाज Mascot सेटिंग्स में कॉन्फ़िगर किया गया है।', diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts index 95e225fbd0..6b2ad23e6d 100644 --- a/app/src/lib/i18n/id.ts +++ b/app/src/lib/i18n/id.ts @@ -4984,6 +4984,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'Tidak dapat memuat SOUL.md', 'settings.persona.soul.saveError': 'Tidak dapat menyimpan SOUL.md', 'settings.persona.soul.resetError': 'Tidak dapat mereset SOUL.md', + 'settings.persona.builder.modeLabel': 'Mode editor persona', + 'settings.persona.builder.modeGuided': 'Terpandu', + 'settings.persona.builder.modeAdvanced': 'Lanjutan', + 'settings.persona.builder.intro': 'Isi beberapa kolom dan kami menuliskannya ke persona Anda. Tanpa markdown.', + 'settings.persona.builder.personalityLabel': 'Kepribadian', + 'settings.persona.builder.personalityPlaceholder': 'mis. Hangat, ingin tahu, dan langsung. Jujur soal ketidakpastian.', + 'settings.persona.builder.voiceLabel': 'Gaya komunikasi', + 'settings.persona.builder.voicePlaceholder': 'mis. Mulai dari jawaban, singkat, dan sesuaikan dengan nada saya.', + 'settings.persona.builder.aboutLabel': 'Tentang Anda', + 'settings.persona.builder.aboutPlaceholder': 'mis. Saya menjalankan studio desain kecil dan lebih suka bahasa yang sederhana.', + 'settings.persona.builder.preservedNote': 'Bagian lain yang Anda tulis sendiri tetap disimpan — beralih ke Lanjutan untuk melihat persona lengkap.', + 'settings.persona.builder.securityNote': 'Ingin memilih apa yang boleh dilakukan asisten?', + 'settings.persona.builder.securityLink': 'Buka Akses agen', 'settings.persona.appearanceHeading': 'Avatar & Suara', 'settings.persona.appearanceDesc': 'Warna Mascot, avatar GIF kustom, dan suara balasan dikonfigurasi dalam pengaturan Mascot.', diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts index 580753ee50..d5a6d2c3cb 100644 --- a/app/src/lib/i18n/it.ts +++ b/app/src/lib/i18n/it.ts @@ -5052,6 +5052,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'Impossibile caricare SOUL.md', 'settings.persona.soul.saveError': 'Impossibile salvare SOUL.md', 'settings.persona.soul.resetError': 'Impossibile reimpostare SOUL.md', + 'settings.persona.builder.modeLabel': 'Modalità editor della persona', + 'settings.persona.builder.modeGuided': 'Guidata', + 'settings.persona.builder.modeAdvanced': 'Avanzata', + 'settings.persona.builder.intro': 'Compila alcuni campi e li scriviamo noi nella tua persona. Nessun markdown richiesto.', + 'settings.persona.builder.personalityLabel': 'Personalità', + 'settings.persona.builder.personalityPlaceholder': 'es. Cordiale, curioso e diretto. Onesto sull’incertezza.', + 'settings.persona.builder.voiceLabel': 'Stile di comunicazione', + 'settings.persona.builder.voicePlaceholder': 'es. Inizia dalla risposta, sii breve e adatta il mio tono.', + 'settings.persona.builder.aboutLabel': 'Su di te', + 'settings.persona.builder.aboutPlaceholder': 'es. Gestisco un piccolo studio di design e preferisco un linguaggio semplice.', + 'settings.persona.builder.preservedNote': 'Le altre sezioni che hai scritto a mano vengono mantenute: passa ad Avanzata per vedere la persona completa.', + 'settings.persona.builder.securityNote': 'Vuoi scegliere cosa può fare l’assistente?', + 'settings.persona.builder.securityLink': 'Apri Accesso agente', 'settings.persona.appearanceHeading': 'Avatar e Voce', 'settings.persona.appearanceDesc': "Il colore della mascotte, l'avatar personalizzato GIF e la voce di risposta sono configurati nelle impostazioni della mascotte.", diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts index 74046271b0..19cbfe54f9 100644 --- a/app/src/lib/i18n/ko.ts +++ b/app/src/lib/i18n/ko.ts @@ -4920,6 +4920,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'SOUL.md를 불러올 수 없습니다', 'settings.persona.soul.saveError': 'SOUL.md를 저장할 수 없습니다', 'settings.persona.soul.resetError': 'SOUL.md를 초기화할 수 없습니다', + 'settings.persona.builder.modeLabel': '페르소나 편집기 모드', + 'settings.persona.builder.modeGuided': '가이드', + 'settings.persona.builder.modeAdvanced': '고급', + 'settings.persona.builder.intro': '몇 가지 항목만 입력하면 페르소나에 대신 작성해 드립니다. 마크다운은 필요 없습니다.', + 'settings.persona.builder.personalityLabel': '성격', + 'settings.persona.builder.personalityPlaceholder': '예: 따뜻하고 호기심 많고 직설적. 불확실할 땐 솔직하게.', + 'settings.persona.builder.voiceLabel': '커뮤니케이션 스타일', + 'settings.persona.builder.voicePlaceholder': '예: 답부터 말하고, 간결하게, 내 말투에 맞춰서.', + 'settings.persona.builder.aboutLabel': '당신에 대해', + 'settings.persona.builder.aboutPlaceholder': '예: 작은 디자인 스튜디오를 운영하며 쉬운 표현을 선호합니다.', + 'settings.persona.builder.preservedNote': '직접 작성한 다른 섹션은 그대로 유지됩니다 — 전체 페르소나를 보려면 고급으로 전환하세요.', + 'settings.persona.builder.securityNote': '어시스턴트가 할 수 있는 일을 정하시겠어요?', + 'settings.persona.builder.securityLink': '에이전트 액세스 열기', 'settings.persona.appearanceHeading': '아바타 및 음성', 'settings.persona.appearanceDesc': '마스코트 색상, 사용자 지정 GIF 아바타, 응답 음성은 마스코트 설정에서 구성합니다.', diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts index ee94ffa56b..cc703b4e1c 100644 --- a/app/src/lib/i18n/pl.ts +++ b/app/src/lib/i18n/pl.ts @@ -5038,6 +5038,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'Nie udało się wczytać SOUL.md', 'settings.persona.soul.saveError': 'Nie udało się zapisać SOUL.md', 'settings.persona.soul.resetError': 'Nie udało się zresetować SOUL.md', + 'settings.persona.builder.modeLabel': 'Tryb edytora persony', + 'settings.persona.builder.modeGuided': 'Prowadzony', + 'settings.persona.builder.modeAdvanced': 'Zaawansowany', + 'settings.persona.builder.intro': 'Wypełnij kilka pól, a my zapiszemy je w Twojej personie. Markdown nie jest potrzebny.', + 'settings.persona.builder.personalityLabel': 'Osobowość', + 'settings.persona.builder.personalityPlaceholder': 'np. Ciepły, ciekawy i bezpośredni. Szczery wobec niepewności.', + 'settings.persona.builder.voiceLabel': 'Styl komunikacji', + 'settings.persona.builder.voicePlaceholder': 'np. Zacznij od odpowiedzi, pisz zwięźle i dopasuj mój ton.', + 'settings.persona.builder.aboutLabel': 'O Tobie', + 'settings.persona.builder.aboutPlaceholder': 'np. Prowadzę małe studio projektowe i wolę prosty język.', + 'settings.persona.builder.preservedNote': 'Wszelkie inne sekcje napisane ręcznie zostają zachowane — przełącz na Zaawansowany, aby zobaczyć pełną personę.', + 'settings.persona.builder.securityNote': 'Chcesz wybrać, co asystent może robić?', + 'settings.persona.builder.securityLink': 'Otwórz Dostęp agenta', 'settings.persona.appearanceHeading': 'Awatar i głos', 'settings.persona.appearanceDesc': 'Kolor maskotki, własny awatar GIF i głos odpowiedzi są konfigurowane w ustawieniach Maskotki.', diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts index 0b60a812b9..6d51ed9ba4 100644 --- a/app/src/lib/i18n/pt.ts +++ b/app/src/lib/i18n/pt.ts @@ -5054,6 +5054,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'Não foi possível carregar SOUL.md', 'settings.persona.soul.saveError': 'Não foi possível salvar SOUL.md', 'settings.persona.soul.resetError': 'Não foi possível resetar SOUL.md', + 'settings.persona.builder.modeLabel': 'Modo do editor de persona', + 'settings.persona.builder.modeGuided': 'Guiado', + 'settings.persona.builder.modeAdvanced': 'Avançado', + 'settings.persona.builder.intro': 'Preencha alguns campos e nós os escrevemos na sua persona. Não é preciso markdown.', + 'settings.persona.builder.personalityLabel': 'Personalidade', + 'settings.persona.builder.personalityPlaceholder': 'ex.: Acolhedor, curioso e direto. Honesto quanto à incerteza.', + 'settings.persona.builder.voiceLabel': 'Estilo de comunicação', + 'settings.persona.builder.voicePlaceholder': 'ex.: Comece pela resposta, seja breve e acompanhe o meu tom.', + 'settings.persona.builder.aboutLabel': 'Sobre você', + 'settings.persona.builder.aboutPlaceholder': 'ex.: Tenho um pequeno estúdio de design e prefiro linguagem simples.', + 'settings.persona.builder.preservedNote': 'Quaisquer outras seções que você escreveu à mão são mantidas — mude para Avançado para ver a persona completa.', + 'settings.persona.builder.securityNote': 'Quer escolher o que o assistente pode fazer?', + 'settings.persona.builder.securityLink': 'Abrir Acesso do agente', 'settings.persona.appearanceHeading': 'Avatar e Voz', 'settings.persona.appearanceDesc': 'A cor do mascote, o avatar personalizado GIF e a voz de resposta são configurados nas configurações do mascote.', diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts index 8f4ac3700b..22d47a55d0 100644 --- a/app/src/lib/i18n/ru.ts +++ b/app/src/lib/i18n/ru.ts @@ -5013,6 +5013,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': 'Не удалось загрузить SOUL.md.', 'settings.persona.soul.saveError': 'Не удалось сохранить SOUL.md.', 'settings.persona.soul.resetError': 'Не удалось сбросить SOUL.md.', + 'settings.persona.builder.modeLabel': 'Режим редактора персоны', + 'settings.persona.builder.modeGuided': 'С подсказками', + 'settings.persona.builder.modeAdvanced': 'Расширенный', + 'settings.persona.builder.intro': 'Заполните несколько полей, и мы впишем их в вашу персону. Markdown не нужен.', + 'settings.persona.builder.personalityLabel': 'Характер', + 'settings.persona.builder.personalityPlaceholder': 'напр. Тёплый, любознательный и прямой. Честен в неопределённости.', + 'settings.persona.builder.voiceLabel': 'Стиль общения', + 'settings.persona.builder.voicePlaceholder': 'напр. Сначала ответ, кратко и в моём тоне.', + 'settings.persona.builder.aboutLabel': 'О вас', + 'settings.persona.builder.aboutPlaceholder': 'напр. У меня небольшая дизайн-студия, предпочитаю простой язык.', + 'settings.persona.builder.preservedNote': 'Все другие разделы, написанные вручную, сохраняются — переключитесь на «Расширенный», чтобы увидеть персону целиком.', + 'settings.persona.builder.securityNote': 'Хотите выбрать, что разрешено ассистенту?', + 'settings.persona.builder.securityLink': 'Открыть доступ агента', 'settings.persona.appearanceHeading': 'Аватар и голос', 'settings.persona.appearanceDesc': 'Цвет талисмана, пользовательский аватар GIF и голос ответа настраиваются в настройках талисмана.', diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts index 53e158ebd8..c679ee2e8c 100644 --- a/app/src/lib/i18n/zh-CN.ts +++ b/app/src/lib/i18n/zh-CN.ts @@ -4724,6 +4724,19 @@ const messages: TranslationMap = { 'settings.persona.soul.loadError': '无法加载 SOUL.md', 'settings.persona.soul.saveError': '无法保存 SOUL.md', 'settings.persona.soul.resetError': '无法重置 SOUL.md', + 'settings.persona.builder.modeLabel': '角色编辑器模式', + 'settings.persona.builder.modeGuided': '引导式', + 'settings.persona.builder.modeAdvanced': '高级', + 'settings.persona.builder.intro': '填写几个字段,我们会替你写入角色设定。无需 Markdown。', + 'settings.persona.builder.personalityLabel': '性格', + 'settings.persona.builder.personalityPlaceholder': '例如:温暖、好奇、直接。对不确定坦诚。', + 'settings.persona.builder.voiceLabel': '沟通风格', + 'settings.persona.builder.voicePlaceholder': '例如:先给答案,简洁,并贴合我的语气。', + 'settings.persona.builder.aboutLabel': '关于你', + 'settings.persona.builder.aboutPlaceholder': '例如:我经营一家小型设计工作室,喜欢通俗的表达。', + 'settings.persona.builder.preservedNote': '你手写的其他部分都会保留——切换到「高级」可查看完整角色设定。', + 'settings.persona.builder.securityNote': '想选择助手可以做什么?', + 'settings.persona.builder.securityLink': '打开代理访问权限', 'settings.persona.appearanceHeading': '头像和声音', 'settings.persona.appearanceDesc': '吉祥物颜色、自定义 GIF 头像和回复声音在吉祥物设置中配置。', 'settings.persona.openMascotSettings': '打开吉祥物设置', From 7c750b4cc681ba431e7eab11c015449daf2b7884 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 2 Jul 2026 19:31:31 +0530 Subject: [PATCH 2/3] style: apply prettier to persona-builder files and i18n locales Prettier-only formatting of the persona builder components/tests and the guided-persona i18n key additions across all locale files. --- .../settings/panels/PersonaPanel.test.tsx | 4 +++- .../panels/persona/PersonaGuidedFields.tsx | 2 +- .../panels/persona/personaSections.test.ts | 6 +----- app/src/lib/i18n/ar.ts | 12 ++++++++---- app/src/lib/i18n/bn.ts | 15 ++++++++++----- app/src/lib/i18n/de.ts | 15 ++++++++++----- app/src/lib/i18n/es.ts | 15 ++++++++++----- app/src/lib/i18n/fr.ts | 18 ++++++++++++------ app/src/lib/i18n/hi.ts | 15 ++++++++++----- app/src/lib/i18n/id.ts | 15 ++++++++++----- app/src/lib/i18n/it.ts | 15 ++++++++++----- app/src/lib/i18n/ko.ts | 12 ++++++++---- app/src/lib/i18n/pl.ts | 15 ++++++++++----- app/src/lib/i18n/pt.ts | 15 ++++++++++----- app/src/lib/i18n/ru.ts | 12 ++++++++---- app/src/lib/i18n/zh-CN.ts | 3 ++- 16 files changed, 123 insertions(+), 66 deletions(-) diff --git a/app/src/components/settings/panels/PersonaPanel.test.tsx b/app/src/components/settings/panels/PersonaPanel.test.tsx index d32b0c81a1..f4dbc33352 100644 --- a/app/src/components/settings/panels/PersonaPanel.test.tsx +++ b/app/src/components/settings/panels/PersonaPanel.test.tsx @@ -54,7 +54,9 @@ describe('PersonaPanel', () => { writePersonaFileMock.mockImplementation((_name: string, contents: string) => Promise.resolve(soulFile({ contents, is_default: false })) ); - resetPersonaFileMock.mockResolvedValue(soulFile({ contents: 'default soul', is_default: true })); + resetPersonaFileMock.mockResolvedValue( + soulFile({ contents: 'default soul', is_default: true }) + ); }); it('defaults to the guided builder and hides raw markdown', async () => { diff --git a/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx b/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx index e520d11248..43076b59ca 100644 --- a/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx +++ b/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx @@ -1,6 +1,6 @@ import { useT } from '../../../../lib/i18n/I18nContext'; -import { useSettingsNavigation } from '../../hooks/useSettingsNavigation'; import { SettingsRow, SettingsTextArea } from '../../controls'; +import { useSettingsNavigation } from '../../hooks/useSettingsNavigation'; import { applyPersonaField, parsePersonaFields, type PersonaFieldKey } from './personaSections'; interface PersonaGuidedFieldsProps { diff --git a/app/src/components/settings/panels/persona/personaSections.test.ts b/app/src/components/settings/panels/persona/personaSections.test.ts index d6238bb703..fa6d189576 100644 --- a/app/src/components/settings/panels/persona/personaSections.test.ts +++ b/app/src/components/settings/panels/persona/personaSections.test.ts @@ -1,10 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { - applyPersonaField, - applyPersonaFields, - parsePersonaFields, -} from './personaSections'; +import { applyPersonaField, applyPersonaFields, parsePersonaFields } from './personaSections'; const SOUL = `# OpenHuman diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts index a2f31478a3..237c8ef6ff 100644 --- a/app/src/lib/i18n/ar.ts +++ b/app/src/lib/i18n/ar.ts @@ -4870,14 +4870,18 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'وضع محرر الشخصية', 'settings.persona.builder.modeGuided': 'موجّه', 'settings.persona.builder.modeAdvanced': 'متقدّم', - 'settings.persona.builder.intro': 'املأ بضعة حقول وسنكتبها في شخصيتك نيابةً عنك. لا حاجة إلى ماركداون.', + 'settings.persona.builder.intro': + 'املأ بضعة حقول وسنكتبها في شخصيتك نيابةً عنك. لا حاجة إلى ماركداون.', 'settings.persona.builder.personalityLabel': 'الشخصية', - 'settings.persona.builder.personalityPlaceholder': 'مثال: ودود وفضولي ومباشر. صادق عند عدم اليقين.', + 'settings.persona.builder.personalityPlaceholder': + 'مثال: ودود وفضولي ومباشر. صادق عند عدم اليقين.', 'settings.persona.builder.voiceLabel': 'أسلوب التواصل', 'settings.persona.builder.voicePlaceholder': 'مثال: ابدأ بالإجابة، واجعلها موجزة، وطابق نبرتي.', 'settings.persona.builder.aboutLabel': 'نبذة عنك', - 'settings.persona.builder.aboutPlaceholder': 'مثال: أدير استوديو تصميم صغيرًا وأفضّل اللغة البسيطة.', - 'settings.persona.builder.preservedNote': 'تُحفظ أي أقسام أخرى كتبتها بنفسك — انتقل إلى «متقدّم» لرؤية الشخصية كاملة.', + 'settings.persona.builder.aboutPlaceholder': + 'مثال: أدير استوديو تصميم صغيرًا وأفضّل اللغة البسيطة.', + 'settings.persona.builder.preservedNote': + 'تُحفظ أي أقسام أخرى كتبتها بنفسك — انتقل إلى «متقدّم» لرؤية الشخصية كاملة.', 'settings.persona.builder.securityNote': 'هل تريد تحديد ما يُسمح للمساعد بفعله؟', 'settings.persona.builder.securityLink': 'فتح وصول الوكيل', 'settings.persona.appearanceHeading': 'صوت الأفاتار', diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts index 3033547adf..ec9e0df745 100644 --- a/app/src/lib/i18n/bn.ts +++ b/app/src/lib/i18n/bn.ts @@ -4970,14 +4970,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'পারসোনা এডিটর মোড', 'settings.persona.builder.modeGuided': 'গাইডেড', 'settings.persona.builder.modeAdvanced': 'অ্যাডভান্সড', - 'settings.persona.builder.intro': 'কয়েকটি ঘর পূরণ করুন, আমরা সেগুলো আপনার পারসোনায় লিখে দেব। মার্কডাউন লাগবে না।', + 'settings.persona.builder.intro': + 'কয়েকটি ঘর পূরণ করুন, আমরা সেগুলো আপনার পারসোনায় লিখে দেব। মার্কডাউন লাগবে না।', 'settings.persona.builder.personalityLabel': 'ব্যক্তিত্ব', - 'settings.persona.builder.personalityPlaceholder': 'যেমন: উষ্ণ, কৌতূহলী ও স্পষ্টবাদী। অনিশ্চয়তায় সৎ।', + 'settings.persona.builder.personalityPlaceholder': + 'যেমন: উষ্ণ, কৌতূহলী ও স্পষ্টবাদী। অনিশ্চয়তায় সৎ।', 'settings.persona.builder.voiceLabel': 'যোগাযোগের ধরন', - 'settings.persona.builder.voicePlaceholder': 'যেমন: আগে উত্তর দিন, সংক্ষিপ্ত রাখুন এবং আমার সুরে মিলিয়ে নিন।', + 'settings.persona.builder.voicePlaceholder': + 'যেমন: আগে উত্তর দিন, সংক্ষিপ্ত রাখুন এবং আমার সুরে মিলিয়ে নিন।', 'settings.persona.builder.aboutLabel': 'আপনার সম্পর্কে', - 'settings.persona.builder.aboutPlaceholder': 'যেমন: আমি একটি ছোট ডিজাইন স্টুডিও চালাই এবং সহজ ভাষা পছন্দ করি।', - 'settings.persona.builder.preservedNote': 'আপনার নিজের হাতে লেখা অন্যান্য অংশ সংরক্ষিত থাকে — সম্পূর্ণ পারসোনা দেখতে অ্যাডভান্সড-এ যান।', + 'settings.persona.builder.aboutPlaceholder': + 'যেমন: আমি একটি ছোট ডিজাইন স্টুডিও চালাই এবং সহজ ভাষা পছন্দ করি।', + 'settings.persona.builder.preservedNote': + 'আপনার নিজের হাতে লেখা অন্যান্য অংশ সংরক্ষিত থাকে — সম্পূর্ণ পারসোনা দেখতে অ্যাডভান্সড-এ যান।', 'settings.persona.builder.securityNote': 'সহকারী কী করতে পারবে তা ঠিক করতে চান?', 'settings.persona.builder.securityLink': 'এজেন্ট অ্যাক্সেস খুলুন', 'settings.persona.appearanceHeading': 'অবতার & ভয়েস', diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts index aa1dd76b32..333074b53d 100644 --- a/app/src/lib/i18n/de.ts +++ b/app/src/lib/i18n/de.ts @@ -5099,14 +5099,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Persona-Editor-Modus', 'settings.persona.builder.modeGuided': 'Geführt', 'settings.persona.builder.modeAdvanced': 'Erweitert', - 'settings.persona.builder.intro': 'Füllen Sie ein paar Felder aus und wir schreiben sie für Sie in Ihre Persona. Kein Markdown nötig.', + 'settings.persona.builder.intro': + 'Füllen Sie ein paar Felder aus und wir schreiben sie für Sie in Ihre Persona. Kein Markdown nötig.', 'settings.persona.builder.personalityLabel': 'Persönlichkeit', - 'settings.persona.builder.personalityPlaceholder': 'z. B. Warmherzig, neugierig und direkt. Ehrlich bei Unsicherheit.', + 'settings.persona.builder.personalityPlaceholder': + 'z. B. Warmherzig, neugierig und direkt. Ehrlich bei Unsicherheit.', 'settings.persona.builder.voiceLabel': 'Kommunikationsstil', - 'settings.persona.builder.voicePlaceholder': 'z. B. Zuerst die Antwort, kurz halten und meinen Ton treffen.', + 'settings.persona.builder.voicePlaceholder': + 'z. B. Zuerst die Antwort, kurz halten und meinen Ton treffen.', 'settings.persona.builder.aboutLabel': 'Über Sie', - 'settings.persona.builder.aboutPlaceholder': 'z. B. Ich leite ein kleines Designstudio und bevorzuge klare Sprache.', - 'settings.persona.builder.preservedNote': 'Alle anderen von Hand geschriebenen Abschnitte bleiben erhalten — wechseln Sie zu „Erweitert“, um die vollständige Persona zu sehen.', + 'settings.persona.builder.aboutPlaceholder': + 'z. B. Ich leite ein kleines Designstudio und bevorzuge klare Sprache.', + 'settings.persona.builder.preservedNote': + 'Alle anderen von Hand geschriebenen Abschnitte bleiben erhalten — wechseln Sie zu „Erweitert“, um die vollständige Persona zu sehen.', 'settings.persona.builder.securityNote': 'Möchten Sie festlegen, was der Assistent tun darf?', 'settings.persona.builder.securityLink': 'Agentenzugriff öffnen', 'settings.persona.appearanceHeading': 'Avatar und Stimme', diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts index 6139a02217..4265877250 100644 --- a/app/src/lib/i18n/es.ts +++ b/app/src/lib/i18n/es.ts @@ -5064,14 +5064,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Modo del editor de persona', 'settings.persona.builder.modeGuided': 'Guiado', 'settings.persona.builder.modeAdvanced': 'Avanzado', - 'settings.persona.builder.intro': 'Rellena unos campos y los escribimos en tu persona por ti. No hace falta markdown.', + 'settings.persona.builder.intro': + 'Rellena unos campos y los escribimos en tu persona por ti. No hace falta markdown.', 'settings.persona.builder.personalityLabel': 'Personalidad', - 'settings.persona.builder.personalityPlaceholder': 'p. ej. Cercano, curioso y directo. Honesto ante la incertidumbre.', + 'settings.persona.builder.personalityPlaceholder': + 'p. ej. Cercano, curioso y directo. Honesto ante la incertidumbre.', 'settings.persona.builder.voiceLabel': 'Estilo de comunicación', - 'settings.persona.builder.voicePlaceholder': 'p. ej. Empieza por la respuesta, sé breve y adapta mi tono.', + 'settings.persona.builder.voicePlaceholder': + 'p. ej. Empieza por la respuesta, sé breve y adapta mi tono.', 'settings.persona.builder.aboutLabel': 'Sobre ti', - 'settings.persona.builder.aboutPlaceholder': 'p. ej. Dirijo un pequeño estudio de diseño y prefiero un lenguaje sencillo.', - 'settings.persona.builder.preservedNote': 'Cualquier otra sección que hayas escrito a mano se conserva: cambia a Avanzado para ver la persona completa.', + 'settings.persona.builder.aboutPlaceholder': + 'p. ej. Dirijo un pequeño estudio de diseño y prefiero un lenguaje sencillo.', + 'settings.persona.builder.preservedNote': + 'Cualquier otra sección que hayas escrito a mano se conserva: cambia a Avanzado para ver la persona completa.', 'settings.persona.builder.securityNote': '¿Quieres elegir lo que el asistente puede hacer?', 'settings.persona.builder.securityLink': 'Abrir Acceso del agente', 'settings.persona.appearanceHeading': 'Avatar y Voz', diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts index f0ca71d230..404de50aa2 100644 --- a/app/src/lib/i18n/fr.ts +++ b/app/src/lib/i18n/fr.ts @@ -5083,15 +5083,21 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Mode de l’éditeur de persona', 'settings.persona.builder.modeGuided': 'Guidé', 'settings.persona.builder.modeAdvanced': 'Avancé', - 'settings.persona.builder.intro': 'Remplissez quelques champs et nous les écrivons dans votre persona. Aucun markdown requis.', + 'settings.persona.builder.intro': + 'Remplissez quelques champs et nous les écrivons dans votre persona. Aucun markdown requis.', 'settings.persona.builder.personalityLabel': 'Personnalité', - 'settings.persona.builder.personalityPlaceholder': 'p. ex. Chaleureux, curieux et direct. Honnête face à l’incertitude.', + 'settings.persona.builder.personalityPlaceholder': + 'p. ex. Chaleureux, curieux et direct. Honnête face à l’incertitude.', 'settings.persona.builder.voiceLabel': 'Style de communication', - 'settings.persona.builder.voicePlaceholder': 'p. ex. Commencez par la réponse, restez bref et adaptez mon ton.', + 'settings.persona.builder.voicePlaceholder': + 'p. ex. Commencez par la réponse, restez bref et adaptez mon ton.', 'settings.persona.builder.aboutLabel': 'À propos de vous', - 'settings.persona.builder.aboutPlaceholder': 'p. ex. Je dirige un petit studio de design et je préfère un langage simple.', - 'settings.persona.builder.preservedNote': 'Toutes les autres sections que vous avez écrites à la main sont conservées — passez en mode Avancé pour voir la persona complète.', - 'settings.persona.builder.securityNote': 'Vous voulez choisir ce que l’assistant est autorisé à faire ?', + 'settings.persona.builder.aboutPlaceholder': + 'p. ex. Je dirige un petit studio de design et je préfère un langage simple.', + 'settings.persona.builder.preservedNote': + 'Toutes les autres sections que vous avez écrites à la main sont conservées — passez en mode Avancé pour voir la persona complète.', + 'settings.persona.builder.securityNote': + 'Vous voulez choisir ce que l’assistant est autorisé à faire ?', 'settings.persona.builder.securityLink': 'Ouvrir Accès de l’agent', 'settings.persona.appearanceHeading': 'Avatar et Voix', 'settings.persona.appearanceDesc': diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts index 592d99d3f2..3c15779aed 100644 --- a/app/src/lib/i18n/hi.ts +++ b/app/src/lib/i18n/hi.ts @@ -4974,14 +4974,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'पर्सोना एडिटर मोड', 'settings.persona.builder.modeGuided': 'निर्देशित', 'settings.persona.builder.modeAdvanced': 'उन्नत', - 'settings.persona.builder.intro': 'कुछ फ़ील्ड भरें और हम उन्हें आपके पर्सोना में लिख देंगे। मार्कडाउन की ज़रूरत नहीं।', + 'settings.persona.builder.intro': + 'कुछ फ़ील्ड भरें और हम उन्हें आपके पर्सोना में लिख देंगे। मार्कडाउन की ज़रूरत नहीं।', 'settings.persona.builder.personalityLabel': 'व्यक्तित्व', - 'settings.persona.builder.personalityPlaceholder': 'जैसे: गर्मजोश, जिज्ञासु और सीधा। अनिश्चितता पर ईमानदार।', + 'settings.persona.builder.personalityPlaceholder': + 'जैसे: गर्मजोश, जिज्ञासु और सीधा। अनिश्चितता पर ईमानदार।', 'settings.persona.builder.voiceLabel': 'संवाद शैली', - 'settings.persona.builder.voicePlaceholder': 'जैसे: पहले उत्तर दें, संक्षिप्त रखें और मेरे लहजे से मेल खाएँ।', + 'settings.persona.builder.voicePlaceholder': + 'जैसे: पहले उत्तर दें, संक्षिप्त रखें और मेरे लहजे से मेल खाएँ।', 'settings.persona.builder.aboutLabel': 'आपके बारे में', - 'settings.persona.builder.aboutPlaceholder': 'जैसे: मैं एक छोटा डिज़ाइन स्टूडियो चलाता हूँ और सरल भाषा पसंद करता हूँ।', - 'settings.persona.builder.preservedNote': 'आपके हाथ से लिखे अन्य अनुभाग सुरक्षित रहते हैं — पूरा पर्सोना देखने के लिए उन्नत पर जाएँ।', + 'settings.persona.builder.aboutPlaceholder': + 'जैसे: मैं एक छोटा डिज़ाइन स्टूडियो चलाता हूँ और सरल भाषा पसंद करता हूँ।', + 'settings.persona.builder.preservedNote': + 'आपके हाथ से लिखे अन्य अनुभाग सुरक्षित रहते हैं — पूरा पर्सोना देखने के लिए उन्नत पर जाएँ।', 'settings.persona.builder.securityNote': 'तय करना चाहते हैं कि असिस्टेंट क्या कर सकता है?', 'settings.persona.builder.securityLink': 'एजेंट एक्सेस खोलें', 'settings.persona.appearanceHeading': 'अवतार और आवाज', diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts index 6b2ad23e6d..c8c8f5294b 100644 --- a/app/src/lib/i18n/id.ts +++ b/app/src/lib/i18n/id.ts @@ -4987,14 +4987,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Mode editor persona', 'settings.persona.builder.modeGuided': 'Terpandu', 'settings.persona.builder.modeAdvanced': 'Lanjutan', - 'settings.persona.builder.intro': 'Isi beberapa kolom dan kami menuliskannya ke persona Anda. Tanpa markdown.', + 'settings.persona.builder.intro': + 'Isi beberapa kolom dan kami menuliskannya ke persona Anda. Tanpa markdown.', 'settings.persona.builder.personalityLabel': 'Kepribadian', - 'settings.persona.builder.personalityPlaceholder': 'mis. Hangat, ingin tahu, dan langsung. Jujur soal ketidakpastian.', + 'settings.persona.builder.personalityPlaceholder': + 'mis. Hangat, ingin tahu, dan langsung. Jujur soal ketidakpastian.', 'settings.persona.builder.voiceLabel': 'Gaya komunikasi', - 'settings.persona.builder.voicePlaceholder': 'mis. Mulai dari jawaban, singkat, dan sesuaikan dengan nada saya.', + 'settings.persona.builder.voicePlaceholder': + 'mis. Mulai dari jawaban, singkat, dan sesuaikan dengan nada saya.', 'settings.persona.builder.aboutLabel': 'Tentang Anda', - 'settings.persona.builder.aboutPlaceholder': 'mis. Saya menjalankan studio desain kecil dan lebih suka bahasa yang sederhana.', - 'settings.persona.builder.preservedNote': 'Bagian lain yang Anda tulis sendiri tetap disimpan — beralih ke Lanjutan untuk melihat persona lengkap.', + 'settings.persona.builder.aboutPlaceholder': + 'mis. Saya menjalankan studio desain kecil dan lebih suka bahasa yang sederhana.', + 'settings.persona.builder.preservedNote': + 'Bagian lain yang Anda tulis sendiri tetap disimpan — beralih ke Lanjutan untuk melihat persona lengkap.', 'settings.persona.builder.securityNote': 'Ingin memilih apa yang boleh dilakukan asisten?', 'settings.persona.builder.securityLink': 'Buka Akses agen', 'settings.persona.appearanceHeading': 'Avatar & Suara', diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts index d5a6d2c3cb..d0b0509693 100644 --- a/app/src/lib/i18n/it.ts +++ b/app/src/lib/i18n/it.ts @@ -5055,14 +5055,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Modalità editor della persona', 'settings.persona.builder.modeGuided': 'Guidata', 'settings.persona.builder.modeAdvanced': 'Avanzata', - 'settings.persona.builder.intro': 'Compila alcuni campi e li scriviamo noi nella tua persona. Nessun markdown richiesto.', + 'settings.persona.builder.intro': + 'Compila alcuni campi e li scriviamo noi nella tua persona. Nessun markdown richiesto.', 'settings.persona.builder.personalityLabel': 'Personalità', - 'settings.persona.builder.personalityPlaceholder': 'es. Cordiale, curioso e diretto. Onesto sull’incertezza.', + 'settings.persona.builder.personalityPlaceholder': + 'es. Cordiale, curioso e diretto. Onesto sull’incertezza.', 'settings.persona.builder.voiceLabel': 'Stile di comunicazione', - 'settings.persona.builder.voicePlaceholder': 'es. Inizia dalla risposta, sii breve e adatta il mio tono.', + 'settings.persona.builder.voicePlaceholder': + 'es. Inizia dalla risposta, sii breve e adatta il mio tono.', 'settings.persona.builder.aboutLabel': 'Su di te', - 'settings.persona.builder.aboutPlaceholder': 'es. Gestisco un piccolo studio di design e preferisco un linguaggio semplice.', - 'settings.persona.builder.preservedNote': 'Le altre sezioni che hai scritto a mano vengono mantenute: passa ad Avanzata per vedere la persona completa.', + 'settings.persona.builder.aboutPlaceholder': + 'es. Gestisco un piccolo studio di design e preferisco un linguaggio semplice.', + 'settings.persona.builder.preservedNote': + 'Le altre sezioni che hai scritto a mano vengono mantenute: passa ad Avanzata per vedere la persona completa.', 'settings.persona.builder.securityNote': 'Vuoi scegliere cosa può fare l’assistente?', 'settings.persona.builder.securityLink': 'Apri Accesso agente', 'settings.persona.appearanceHeading': 'Avatar e Voce', diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts index 19cbfe54f9..a439055669 100644 --- a/app/src/lib/i18n/ko.ts +++ b/app/src/lib/i18n/ko.ts @@ -4923,14 +4923,18 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': '페르소나 편집기 모드', 'settings.persona.builder.modeGuided': '가이드', 'settings.persona.builder.modeAdvanced': '고급', - 'settings.persona.builder.intro': '몇 가지 항목만 입력하면 페르소나에 대신 작성해 드립니다. 마크다운은 필요 없습니다.', + 'settings.persona.builder.intro': + '몇 가지 항목만 입력하면 페르소나에 대신 작성해 드립니다. 마크다운은 필요 없습니다.', 'settings.persona.builder.personalityLabel': '성격', - 'settings.persona.builder.personalityPlaceholder': '예: 따뜻하고 호기심 많고 직설적. 불확실할 땐 솔직하게.', + 'settings.persona.builder.personalityPlaceholder': + '예: 따뜻하고 호기심 많고 직설적. 불확실할 땐 솔직하게.', 'settings.persona.builder.voiceLabel': '커뮤니케이션 스타일', 'settings.persona.builder.voicePlaceholder': '예: 답부터 말하고, 간결하게, 내 말투에 맞춰서.', 'settings.persona.builder.aboutLabel': '당신에 대해', - 'settings.persona.builder.aboutPlaceholder': '예: 작은 디자인 스튜디오를 운영하며 쉬운 표현을 선호합니다.', - 'settings.persona.builder.preservedNote': '직접 작성한 다른 섹션은 그대로 유지됩니다 — 전체 페르소나를 보려면 고급으로 전환하세요.', + 'settings.persona.builder.aboutPlaceholder': + '예: 작은 디자인 스튜디오를 운영하며 쉬운 표현을 선호합니다.', + 'settings.persona.builder.preservedNote': + '직접 작성한 다른 섹션은 그대로 유지됩니다 — 전체 페르소나를 보려면 고급으로 전환하세요.', 'settings.persona.builder.securityNote': '어시스턴트가 할 수 있는 일을 정하시겠어요?', 'settings.persona.builder.securityLink': '에이전트 액세스 열기', 'settings.persona.appearanceHeading': '아바타 및 음성', diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts index cc703b4e1c..2450ee1997 100644 --- a/app/src/lib/i18n/pl.ts +++ b/app/src/lib/i18n/pl.ts @@ -5041,14 +5041,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Tryb edytora persony', 'settings.persona.builder.modeGuided': 'Prowadzony', 'settings.persona.builder.modeAdvanced': 'Zaawansowany', - 'settings.persona.builder.intro': 'Wypełnij kilka pól, a my zapiszemy je w Twojej personie. Markdown nie jest potrzebny.', + 'settings.persona.builder.intro': + 'Wypełnij kilka pól, a my zapiszemy je w Twojej personie. Markdown nie jest potrzebny.', 'settings.persona.builder.personalityLabel': 'Osobowość', - 'settings.persona.builder.personalityPlaceholder': 'np. Ciepły, ciekawy i bezpośredni. Szczery wobec niepewności.', + 'settings.persona.builder.personalityPlaceholder': + 'np. Ciepły, ciekawy i bezpośredni. Szczery wobec niepewności.', 'settings.persona.builder.voiceLabel': 'Styl komunikacji', - 'settings.persona.builder.voicePlaceholder': 'np. Zacznij od odpowiedzi, pisz zwięźle i dopasuj mój ton.', + 'settings.persona.builder.voicePlaceholder': + 'np. Zacznij od odpowiedzi, pisz zwięźle i dopasuj mój ton.', 'settings.persona.builder.aboutLabel': 'O Tobie', - 'settings.persona.builder.aboutPlaceholder': 'np. Prowadzę małe studio projektowe i wolę prosty język.', - 'settings.persona.builder.preservedNote': 'Wszelkie inne sekcje napisane ręcznie zostają zachowane — przełącz na Zaawansowany, aby zobaczyć pełną personę.', + 'settings.persona.builder.aboutPlaceholder': + 'np. Prowadzę małe studio projektowe i wolę prosty język.', + 'settings.persona.builder.preservedNote': + 'Wszelkie inne sekcje napisane ręcznie zostają zachowane — przełącz na Zaawansowany, aby zobaczyć pełną personę.', 'settings.persona.builder.securityNote': 'Chcesz wybrać, co asystent może robić?', 'settings.persona.builder.securityLink': 'Otwórz Dostęp agenta', 'settings.persona.appearanceHeading': 'Awatar i głos', diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts index 6d51ed9ba4..ceabfff91a 100644 --- a/app/src/lib/i18n/pt.ts +++ b/app/src/lib/i18n/pt.ts @@ -5057,14 +5057,19 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Modo do editor de persona', 'settings.persona.builder.modeGuided': 'Guiado', 'settings.persona.builder.modeAdvanced': 'Avançado', - 'settings.persona.builder.intro': 'Preencha alguns campos e nós os escrevemos na sua persona. Não é preciso markdown.', + 'settings.persona.builder.intro': + 'Preencha alguns campos e nós os escrevemos na sua persona. Não é preciso markdown.', 'settings.persona.builder.personalityLabel': 'Personalidade', - 'settings.persona.builder.personalityPlaceholder': 'ex.: Acolhedor, curioso e direto. Honesto quanto à incerteza.', + 'settings.persona.builder.personalityPlaceholder': + 'ex.: Acolhedor, curioso e direto. Honesto quanto à incerteza.', 'settings.persona.builder.voiceLabel': 'Estilo de comunicação', - 'settings.persona.builder.voicePlaceholder': 'ex.: Comece pela resposta, seja breve e acompanhe o meu tom.', + 'settings.persona.builder.voicePlaceholder': + 'ex.: Comece pela resposta, seja breve e acompanhe o meu tom.', 'settings.persona.builder.aboutLabel': 'Sobre você', - 'settings.persona.builder.aboutPlaceholder': 'ex.: Tenho um pequeno estúdio de design e prefiro linguagem simples.', - 'settings.persona.builder.preservedNote': 'Quaisquer outras seções que você escreveu à mão são mantidas — mude para Avançado para ver a persona completa.', + 'settings.persona.builder.aboutPlaceholder': + 'ex.: Tenho um pequeno estúdio de design e prefiro linguagem simples.', + 'settings.persona.builder.preservedNote': + 'Quaisquer outras seções que você escreveu à mão são mantidas — mude para Avançado para ver a persona completa.', 'settings.persona.builder.securityNote': 'Quer escolher o que o assistente pode fazer?', 'settings.persona.builder.securityLink': 'Abrir Acesso do agente', 'settings.persona.appearanceHeading': 'Avatar e Voz', diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts index 22d47a55d0..598fcfafdb 100644 --- a/app/src/lib/i18n/ru.ts +++ b/app/src/lib/i18n/ru.ts @@ -5016,14 +5016,18 @@ const messages: TranslationMap = { 'settings.persona.builder.modeLabel': 'Режим редактора персоны', 'settings.persona.builder.modeGuided': 'С подсказками', 'settings.persona.builder.modeAdvanced': 'Расширенный', - 'settings.persona.builder.intro': 'Заполните несколько полей, и мы впишем их в вашу персону. Markdown не нужен.', + 'settings.persona.builder.intro': + 'Заполните несколько полей, и мы впишем их в вашу персону. Markdown не нужен.', 'settings.persona.builder.personalityLabel': 'Характер', - 'settings.persona.builder.personalityPlaceholder': 'напр. Тёплый, любознательный и прямой. Честен в неопределённости.', + 'settings.persona.builder.personalityPlaceholder': + 'напр. Тёплый, любознательный и прямой. Честен в неопределённости.', 'settings.persona.builder.voiceLabel': 'Стиль общения', 'settings.persona.builder.voicePlaceholder': 'напр. Сначала ответ, кратко и в моём тоне.', 'settings.persona.builder.aboutLabel': 'О вас', - 'settings.persona.builder.aboutPlaceholder': 'напр. У меня небольшая дизайн-студия, предпочитаю простой язык.', - 'settings.persona.builder.preservedNote': 'Все другие разделы, написанные вручную, сохраняются — переключитесь на «Расширенный», чтобы увидеть персону целиком.', + 'settings.persona.builder.aboutPlaceholder': + 'напр. У меня небольшая дизайн-студия, предпочитаю простой язык.', + 'settings.persona.builder.preservedNote': + 'Все другие разделы, написанные вручную, сохраняются — переключитесь на «Расширенный», чтобы увидеть персону целиком.', 'settings.persona.builder.securityNote': 'Хотите выбрать, что разрешено ассистенту?', 'settings.persona.builder.securityLink': 'Открыть доступ агента', 'settings.persona.appearanceHeading': 'Аватар и голос', diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts index c679ee2e8c..39cdad3bf3 100644 --- a/app/src/lib/i18n/zh-CN.ts +++ b/app/src/lib/i18n/zh-CN.ts @@ -4734,7 +4734,8 @@ const messages: TranslationMap = { 'settings.persona.builder.voicePlaceholder': '例如:先给答案,简洁,并贴合我的语气。', 'settings.persona.builder.aboutLabel': '关于你', 'settings.persona.builder.aboutPlaceholder': '例如:我经营一家小型设计工作室,喜欢通俗的表达。', - 'settings.persona.builder.preservedNote': '你手写的其他部分都会保留——切换到「高级」可查看完整角色设定。', + 'settings.persona.builder.preservedNote': + '你手写的其他部分都会保留——切换到「高级」可查看完整角色设定。', 'settings.persona.builder.securityNote': '想选择助手可以做什么?', 'settings.persona.builder.securityLink': '打开代理访问权限', 'settings.persona.appearanceHeading': '头像和声音', From 0706ecb0feae089b9c2e9ab5122276be91b1c5fc Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 2 Jul 2026 17:36:24 +0530 Subject: [PATCH 3/3] feat(persona): role templates for the guided persona builder (#4253) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add six role starting-points (doctor, researcher, executive, teacher, student, family) to the guided persona builder. Applying a template seeds the Personality and Communication-style sections of SOUL.md via the existing splice logic and leaves the user-owned About You section and any hand-written content untouched — a non-destructive starting point the user edits and saves. Seed prose is kept as English constants (editable file content, like the bundled default SOUL.md); only the picker labels/descriptions are localized. Part of the phased #4253 work (PR2 of N); stacked on the persona builder. --- .../settings/panels/PersonaPanel.test.tsx | 18 +++ .../panels/persona/PersonaGuidedFields.tsx | 3 + .../panels/persona/PersonaTemplatePicker.tsx | 57 ++++++++ .../panels/persona/personaTemplates.test.ts | 62 +++++++++ .../panels/persona/personaTemplates.ts | 128 ++++++++++++++++++ app/src/lib/i18n/ar.ts | 15 ++ app/src/lib/i18n/bn.ts | 15 ++ app/src/lib/i18n/de.ts | 15 ++ app/src/lib/i18n/en.ts | 15 ++ app/src/lib/i18n/es.ts | 15 ++ app/src/lib/i18n/fr.ts | 15 ++ app/src/lib/i18n/hi.ts | 15 ++ app/src/lib/i18n/id.ts | 15 ++ app/src/lib/i18n/it.ts | 15 ++ app/src/lib/i18n/ko.ts | 15 ++ app/src/lib/i18n/pl.ts | 15 ++ app/src/lib/i18n/pt.ts | 15 ++ app/src/lib/i18n/ru.ts | 17 +++ app/src/lib/i18n/zh-CN.ts | 15 ++ 19 files changed, 480 insertions(+) create mode 100644 app/src/components/settings/panels/persona/PersonaTemplatePicker.tsx create mode 100644 app/src/components/settings/panels/persona/personaTemplates.test.ts create mode 100644 app/src/components/settings/panels/persona/personaTemplates.ts diff --git a/app/src/components/settings/panels/PersonaPanel.test.tsx b/app/src/components/settings/panels/PersonaPanel.test.tsx index f4dbc33352..d036d33ba4 100644 --- a/app/src/components/settings/panels/PersonaPanel.test.tsx +++ b/app/src/components/settings/panels/PersonaPanel.test.tsx @@ -97,6 +97,24 @@ describe('PersonaPanel', () => { }); }); + it('applies a role template and saves the seeded persona over RPC', async () => { + readPersonaFileMock.mockResolvedValue( + soulFile({ contents: '## Personality\n\nOld.\n', is_default: false }) + ); + renderWithProviders(); + await awaitLoaded(); + + fireEvent.click(screen.getByTestId('persona-template-doctor')); + fireEvent.click(screen.getByTestId('persona-soul-save')); + + await waitFor(() => { + const lastCall = writePersonaFileMock.mock.calls.at(-1); + expect(lastCall?.[0]).toBe('SOUL.md'); + expect(lastCall?.[1]).toContain('Careful and precise'); + expect(lastCall?.[1]).toContain('## Voice'); + }); + }); + it('persists the display name to the store on save', async () => { const { store } = renderWithProviders(); await awaitLoaded(); diff --git a/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx b/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx index 43076b59ca..17bf230b66 100644 --- a/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx +++ b/app/src/components/settings/panels/persona/PersonaGuidedFields.tsx @@ -2,6 +2,7 @@ import { useT } from '../../../../lib/i18n/I18nContext'; import { SettingsRow, SettingsTextArea } from '../../controls'; import { useSettingsNavigation } from '../../hooks/useSettingsNavigation'; import { applyPersonaField, parsePersonaFields, type PersonaFieldKey } from './personaSections'; +import PersonaTemplatePicker from './PersonaTemplatePicker'; interface PersonaGuidedFieldsProps { /** The raw SOUL.md text — the single source of truth this view edits. */ @@ -56,6 +57,8 @@ const PersonaGuidedFields = ({ value, onChange, disabled = false }: PersonaGuide {t('settings.persona.builder.intro')}

+ + {FIELDS.map(field => ( void; + disabled?: boolean; +} + +/** + * Role starting-points for the guided persona builder (issue #4253, PR2). + * + * Applying a template fills the Personality and Communication-style fields for a + * common role (doctor, researcher, executive, teacher, student, family) and + * leaves the rest of SOUL.md — including the user-specific "About you" — intact. + * Nothing is persisted until the user saves, so this is a safe starting point. + */ +const PersonaTemplatePicker = ({ + value, + onChange, + disabled = false, +}: PersonaTemplatePickerProps) => { + const { t } = useT(); + + return ( +
+
+

+ {t('settings.persona.templates.heading')} +

+

+ {t('settings.persona.templates.desc')} +

+
+
+ {PERSONA_TEMPLATES.map(template => ( + + ))} +
+
+ ); +}; + +export default PersonaTemplatePicker; diff --git a/app/src/components/settings/panels/persona/personaTemplates.test.ts b/app/src/components/settings/panels/persona/personaTemplates.test.ts new file mode 100644 index 0000000000..6d50d45f98 --- /dev/null +++ b/app/src/components/settings/panels/persona/personaTemplates.test.ts @@ -0,0 +1,62 @@ +import { describe, expect, it } from 'vitest'; + +import { parsePersonaFields } from './personaSections'; +import { applyTemplate, PERSONA_TEMPLATES } from './personaTemplates'; + +const SOUL = `# OpenHuman + +You are OpenHuman. + +## Personality + +- Old trait. + +## Voice + +- Old voice. + +## When things go wrong + +- Own it. +`; + +describe('PERSONA_TEMPLATES', () => { + it('covers the six requested roles with unique ids and content', () => { + const ids = PERSONA_TEMPLATES.map(t => t.id); + expect(ids).toEqual(['doctor', 'researcher', 'executive', 'teacher', 'student', 'family']); + expect(new Set(ids).size).toBe(ids.length); + for (const tpl of PERSONA_TEMPLATES) { + expect(tpl.fields.personality.trim().length).toBeGreaterThan(0); + expect(tpl.fields.voice.trim().length).toBeGreaterThan(0); + expect(tpl.labelKey).toMatch(/^settings\.persona\.templates\./); + expect(tpl.descriptionKey).toMatch(/^settings\.persona\.templates\./); + } + }); +}); + +describe('applyTemplate', () => { + const doctor = PERSONA_TEMPLATES[0]; + + it('writes the template Personality and Voice into SOUL.md', () => { + const next = applyTemplate(SOUL, doctor); + const fields = parsePersonaFields(next); + expect(fields.personality).toBe(doctor.fields.personality); + expect(fields.voice).toBe(doctor.fields.voice); + }); + + it('preserves unmanaged sections and the user-owned About You section', () => { + const withAbout = applyTemplate(SOUL, doctor).replace( + /\n*$/, + '\n\n## About You\n\nI am a nurse.\n' + ); + const next = applyTemplate(withAbout, PERSONA_TEMPLATES[2]); // executive + expect(next).toContain('## When things go wrong\n\n- Own it.'); + expect(parsePersonaFields(next).about).toBe('I am a nurse.'); + expect(parsePersonaFields(next).personality).toBe(PERSONA_TEMPLATES[2].fields.personality); + }); + + it('is idempotent when the same template is applied twice', () => { + const once = applyTemplate(SOUL, doctor); + expect(applyTemplate(once, doctor)).toBe(once); + }); +}); diff --git a/app/src/components/settings/panels/persona/personaTemplates.ts b/app/src/components/settings/panels/persona/personaTemplates.ts new file mode 100644 index 0000000000..c1b16e535d --- /dev/null +++ b/app/src/components/settings/panels/persona/personaTemplates.ts @@ -0,0 +1,128 @@ +/** + * Role starting-points for the guided persona builder (issue #4253, PR2). + * + * A template seeds the assistant's *character* — the `Personality` and + * `Communication style` (`Voice`) sections of SOUL.md — for a common role. + * Applying one splices those two sections via {@link applyPersonaField} and + * leaves everything else (including the user-specific `About You` section) + * untouched, so a template is a non-destructive starting point the user then + * edits and saves. + * + * The seed prose is intentionally kept as English constants rather than + * translated i18n: it is written into SOUL.md as editable content (the bundled + * default SOUL.md is English too), not UI chrome. Only the picker's labels and + * descriptions are localized. + */ +import { applyPersonaField } from './personaSections'; + +export interface PersonaTemplate { + id: string; + labelKey: string; + descriptionKey: string; + fields: { personality: string; voice: string }; +} + +export const PERSONA_TEMPLATES: readonly PersonaTemplate[] = [ + { + id: 'doctor', + labelKey: 'settings.persona.templates.doctor.label', + descriptionKey: 'settings.persona.templates.doctor.desc', + fields: { + personality: [ + '- Careful and precise; accuracy always beats speed.', + '- Flags uncertainty openly and never guesses at clinical facts.', + '- Cites sources and reminds the user to verify anything clinical.', + '- Not a medical device: never presents output as diagnosis or treatment.', + ].join('\n'), + voice: [ + '- Lead with the answer, then the reasoning and caveats.', + '- Use plain, respectful language; define jargon when it helps.', + '- State how confident you are and what would change the answer.', + ].join('\n'), + }, + }, + { + id: 'researcher', + labelKey: 'settings.persona.templates.researcher.label', + descriptionKey: 'settings.persona.templates.researcher.desc', + fields: { + personality: [ + '- Rigorous and evidence-first; separate what is known from what is assumed.', + '- Structure findings clearly and keep a list of open questions.', + "- Comfortable saying 'the evidence is thin here.'", + ].join('\n'), + voice: [ + '- Summarize the finding first, then the detail with sources.', + '- Use precise terms; state assumptions and limitations explicitly.', + ].join('\n'), + }, + }, + { + id: 'executive', + labelKey: 'settings.persona.templates.executive.label', + descriptionKey: 'settings.persona.templates.executive.desc', + fields: { + personality: [ + "- Concise and decisive; optimize for the user's time.", + '- Surface the recommendation and the trade-offs, then get out of the way.', + '- Proactive about next steps and who owns them.', + ].join('\n'), + voice: [ + '- Lead with the bottom line in one sentence.', + '- Bullet the options with clear pros and cons; skip filler.', + ].join('\n'), + }, + }, + { + id: 'teacher', + labelKey: 'settings.persona.templates.teacher.label', + descriptionKey: 'settings.persona.templates.teacher.desc', + fields: { + personality: [ + '- Patient and encouraging; meet the learner where they are.', + '- Explain step by step and check understanding along the way.', + '- Turn mistakes into teaching moments.', + ].join('\n'), + voice: [ + '- Use simple language and concrete examples.', + '- Break problems into small steps and invite questions.', + ].join('\n'), + }, + }, + { + id: 'student', + labelKey: 'settings.persona.templates.student.label', + descriptionKey: 'settings.persona.templates.student.desc', + fields: { + personality: [ + '- Encouraging study partner; keep things approachable.', + '- Explain in plain language and quiz to reinforce learning.', + '- Honest when something is beyond what you know.', + ].join('\n'), + voice: [ + '- Keep it friendly and concrete.', + '- Give a short explanation, then a quick check-question.', + ].join('\n'), + }, + }, + { + id: 'family', + labelKey: 'settings.persona.templates.family.label', + descriptionKey: 'settings.persona.templates.family.desc', + fields: { + personality: [ + '- Warm, friendly, and helpful for the whole household.', + '- Keep language simple and kind; suitable for all ages.', + '- Redirect anything unsafe and encourage asking a parent when relevant.', + ].join('\n'), + voice: ['- Conversational and clear; avoid jargon.', '- Be brief and positive.'].join('\n'), + }, + }, +] as const; + +/** Splice a template's Personality and Communication-style sections into SOUL.md. */ +export function applyTemplate(soul: string, template: PersonaTemplate): string { + let next = applyPersonaField(soul, 'personality', template.fields.personality); + next = applyPersonaField(next, 'voice', template.fields.voice); + return next; +} diff --git a/app/src/lib/i18n/ar.ts b/app/src/lib/i18n/ar.ts index 237c8ef6ff..c5b9c4893c 100644 --- a/app/src/lib/i18n/ar.ts +++ b/app/src/lib/i18n/ar.ts @@ -4884,6 +4884,21 @@ const messages: TranslationMap = { 'تُحفظ أي أقسام أخرى كتبتها بنفسك — انتقل إلى «متقدّم» لرؤية الشخصية كاملة.', 'settings.persona.builder.securityNote': 'هل تريد تحديد ما يُسمح للمساعد بفعله؟', 'settings.persona.builder.securityLink': 'فتح وصول الوكيل', + 'settings.persona.templates.heading': 'ابدأ من قالب', + 'settings.persona.templates.desc': + 'اختر نقطة بداية — تملأ الشخصية وأسلوب التواصل. يمكنك تعديل كل شيء لاحقًا.', + 'settings.persona.templates.doctor.label': 'مساعد سريري', + 'settings.persona.templates.doctor.desc': 'دقيق، يذكر المصادر، ينبّه إلى عدم اليقين', + 'settings.persona.templates.researcher.label': 'مساعد بحثي', + 'settings.persona.templates.researcher.desc': 'صارم، منظّم، يعتمد على الأدلة', + 'settings.persona.templates.executive.label': 'مساعد تنفيذي', + 'settings.persona.templates.executive.desc': 'موجز، حاسم، موجّه نحو العمل', + 'settings.persona.templates.teacher.label': 'معلّم', + 'settings.persona.templates.teacher.desc': 'صبور، يشرح خطوة بخطوة', + 'settings.persona.templates.student.label': 'رفيق دراسة', + 'settings.persona.templates.student.desc': 'محفّز، يطرح أسئلة، لغة بسيطة', + 'settings.persona.templates.family.label': 'مساعد عائلي', + 'settings.persona.templates.family.desc': 'ودود، لطيف، آمن لكل الأعمار', 'settings.persona.appearanceHeading': 'صوت الأفاتار', 'settings.persona.appearanceDesc': 'لون الماسكوت، العرف Xqx0x avatar، وصوت الرد مصمم في أماكن مكوت.', diff --git a/app/src/lib/i18n/bn.ts b/app/src/lib/i18n/bn.ts index ec9e0df745..0097be7361 100644 --- a/app/src/lib/i18n/bn.ts +++ b/app/src/lib/i18n/bn.ts @@ -4985,6 +4985,21 @@ const messages: TranslationMap = { 'আপনার নিজের হাতে লেখা অন্যান্য অংশ সংরক্ষিত থাকে — সম্পূর্ণ পারসোনা দেখতে অ্যাডভান্সড-এ যান।', 'settings.persona.builder.securityNote': 'সহকারী কী করতে পারবে তা ঠিক করতে চান?', 'settings.persona.builder.securityLink': 'এজেন্ট অ্যাক্সেস খুলুন', + 'settings.persona.templates.heading': 'টেমপ্লেট থেকে শুরু করুন', + 'settings.persona.templates.desc': + 'একটি শুরুর বিন্দু বেছে নিন — এটি ব্যক্তিত্ব ও যোগাযোগের ধরন পূরণ করে। পরে সব কিছু সম্পাদনা করতে পারবেন।', + 'settings.persona.templates.doctor.label': 'ক্লিনিকাল সহকারী', + 'settings.persona.templates.doctor.desc': 'সতর্ক, সূত্র উল্লেখ করে, অনিশ্চয়তা চিহ্নিত করে', + 'settings.persona.templates.researcher.label': 'গবেষণা সহকারী', + 'settings.persona.templates.researcher.desc': 'নিখুঁত, সুসংগঠিত, প্রমাণ-নির্ভর', + 'settings.persona.templates.executive.label': 'নির্বাহী সহকারী', + 'settings.persona.templates.executive.desc': 'সংক্ষিপ্ত, দৃঢ়, কর্ম-কেন্দ্রিক', + 'settings.persona.templates.teacher.label': 'শিক্ষক', + 'settings.persona.templates.teacher.desc': 'ধৈর্যশীল, ধাপে ধাপে ব্যাখ্যা করে', + 'settings.persona.templates.student.label': 'পড়ার সঙ্গী', + 'settings.persona.templates.student.desc': 'উৎসাহদায়ক, কুইজ করে, সহজ ভাষা', + 'settings.persona.templates.family.label': 'পারিবারিক সহকারী', + 'settings.persona.templates.family.desc': 'উষ্ণ, বন্ধুত্বপূর্ণ, সব বয়সের জন্য নিরাপদ', 'settings.persona.appearanceHeading': 'অবতার & ভয়েস', 'settings.persona.appearanceDesc': 'Mascot রঙের রং, স্বনির্ধারিত xxqxqx অ্যাভাটার, এবং Scotox বৈশিষ্ট্যের মধ্য থেকে ভয়েস কনফিগার করা হয়েছে।', diff --git a/app/src/lib/i18n/de.ts b/app/src/lib/i18n/de.ts index 333074b53d..d3e7a8f889 100644 --- a/app/src/lib/i18n/de.ts +++ b/app/src/lib/i18n/de.ts @@ -5114,6 +5114,21 @@ const messages: TranslationMap = { 'Alle anderen von Hand geschriebenen Abschnitte bleiben erhalten — wechseln Sie zu „Erweitert“, um die vollständige Persona zu sehen.', 'settings.persona.builder.securityNote': 'Möchten Sie festlegen, was der Assistent tun darf?', 'settings.persona.builder.securityLink': 'Agentenzugriff öffnen', + 'settings.persona.templates.heading': 'Mit einer Vorlage beginnen', + 'settings.persona.templates.desc': + 'Wählen Sie einen Ausgangspunkt — er füllt Persönlichkeit und Kommunikationsstil. Danach können Sie alles bearbeiten.', + 'settings.persona.templates.doctor.label': 'Klinischer Assistent', + 'settings.persona.templates.doctor.desc': 'Sorgfältig, nennt Quellen, weist auf Unsicherheit hin', + 'settings.persona.templates.researcher.label': 'Forschungsassistent', + 'settings.persona.templates.researcher.desc': 'Gründlich, strukturiert, evidenzbasiert', + 'settings.persona.templates.executive.label': 'Assistent für Führungskräfte', + 'settings.persona.templates.executive.desc': 'Prägnant, entschlossen, handlungsorientiert', + 'settings.persona.templates.teacher.label': 'Lehrkraft', + 'settings.persona.templates.teacher.desc': 'Geduldig, erklärt Schritt für Schritt', + 'settings.persona.templates.student.label': 'Lernbegleiter', + 'settings.persona.templates.student.desc': 'Ermutigend, stellt Quizfragen, einfache Sprache', + 'settings.persona.templates.family.label': 'Familienassistent', + 'settings.persona.templates.family.desc': 'Herzlich, freundlich, für alle Altersgruppen geeignet', 'settings.persona.appearanceHeading': 'Avatar und Stimme', 'settings.persona.appearanceDesc': 'Maskottchenfarbe, benutzerdefinierter GIF-Avatar und Antwortstimme werden in den Maskottcheneinstellungen konfiguriert.', diff --git a/app/src/lib/i18n/en.ts b/app/src/lib/i18n/en.ts index 11bf16309c..a27beb5f6c 100644 --- a/app/src/lib/i18n/en.ts +++ b/app/src/lib/i18n/en.ts @@ -5625,6 +5625,21 @@ const en: TranslationMap = { 'Any other sections you wrote by hand are kept — switch to Advanced to see the full persona.', 'settings.persona.builder.securityNote': 'Choosing what the assistant is allowed to do?', 'settings.persona.builder.securityLink': 'Open Agent access', + 'settings.persona.templates.heading': 'Start from a template', + 'settings.persona.templates.desc': + 'Pick a starting point — it fills Personality and Communication style. You can edit everything afterwards.', + 'settings.persona.templates.doctor.label': 'Clinical assistant', + 'settings.persona.templates.doctor.desc': 'Careful, cites sources, flags uncertainty', + 'settings.persona.templates.researcher.label': 'Research assistant', + 'settings.persona.templates.researcher.desc': 'Rigorous, structured, evidence-first', + 'settings.persona.templates.executive.label': 'Executive assistant', + 'settings.persona.templates.executive.desc': 'Concise, decisive, action-oriented', + 'settings.persona.templates.teacher.label': 'Teacher', + 'settings.persona.templates.teacher.desc': 'Patient, explains step by step', + 'settings.persona.templates.student.label': 'Study buddy', + 'settings.persona.templates.student.desc': 'Encouraging, quizzes, plain language', + 'settings.persona.templates.family.label': 'Family assistant', + 'settings.persona.templates.family.desc': 'Warm, friendly, safe for all ages', 'settings.persona.appearanceHeading': 'Avatar & Voice', 'settings.persona.appearanceDesc': 'Mascot color, custom GIF avatar, and reply voice are configured in Mascot settings.', diff --git a/app/src/lib/i18n/es.ts b/app/src/lib/i18n/es.ts index 4265877250..641b869fc7 100644 --- a/app/src/lib/i18n/es.ts +++ b/app/src/lib/i18n/es.ts @@ -5079,6 +5079,21 @@ const messages: TranslationMap = { 'Cualquier otra sección que hayas escrito a mano se conserva: cambia a Avanzado para ver la persona completa.', 'settings.persona.builder.securityNote': '¿Quieres elegir lo que el asistente puede hacer?', 'settings.persona.builder.securityLink': 'Abrir Acceso del agente', + 'settings.persona.templates.heading': 'Empezar con una plantilla', + 'settings.persona.templates.desc': + 'Elige un punto de partida: rellena Personalidad y Estilo de comunicación. Después puedes editarlo todo.', + 'settings.persona.templates.doctor.label': 'Asistente clínico', + 'settings.persona.templates.doctor.desc': 'Cuidadoso, cita fuentes, señala la incertidumbre', + 'settings.persona.templates.researcher.label': 'Asistente de investigación', + 'settings.persona.templates.researcher.desc': 'Riguroso, estructurado, basado en evidencia', + 'settings.persona.templates.executive.label': 'Asistente ejecutivo', + 'settings.persona.templates.executive.desc': 'Conciso, decidido, orientado a la acción', + 'settings.persona.templates.teacher.label': 'Docente', + 'settings.persona.templates.teacher.desc': 'Paciente, explica paso a paso', + 'settings.persona.templates.student.label': 'Compañero de estudio', + 'settings.persona.templates.student.desc': 'Alentador, hace preguntas, lenguaje sencillo', + 'settings.persona.templates.family.label': 'Asistente familiar', + 'settings.persona.templates.family.desc': 'Cálido, amable, seguro para todas las edades', 'settings.persona.appearanceHeading': 'Avatar y Voz', 'settings.persona.appearanceDesc': 'El color de la mascota, el avatar personalizado GIF y la voz de respuesta se configuran en los ajustes de la mascota.', diff --git a/app/src/lib/i18n/fr.ts b/app/src/lib/i18n/fr.ts index 404de50aa2..b5b8de163d 100644 --- a/app/src/lib/i18n/fr.ts +++ b/app/src/lib/i18n/fr.ts @@ -5099,6 +5099,21 @@ const messages: TranslationMap = { 'settings.persona.builder.securityNote': 'Vous voulez choisir ce que l’assistant est autorisé à faire ?', 'settings.persona.builder.securityLink': 'Ouvrir Accès de l’agent', + 'settings.persona.templates.heading': 'Partir d’un modèle', + 'settings.persona.templates.desc': + 'Choisissez un point de départ : il remplit Personnalité et Style de communication. Vous pourrez tout modifier ensuite.', + 'settings.persona.templates.doctor.label': 'Assistant clinique', + 'settings.persona.templates.doctor.desc': 'Prudent, cite ses sources, signale l’incertitude', + 'settings.persona.templates.researcher.label': 'Assistant de recherche', + 'settings.persona.templates.researcher.desc': 'Rigoureux, structuré, fondé sur des preuves', + 'settings.persona.templates.executive.label': 'Assistant de direction', + 'settings.persona.templates.executive.desc': 'Concis, décisif, orienté action', + 'settings.persona.templates.teacher.label': 'Enseignant', + 'settings.persona.templates.teacher.desc': 'Patient, explique étape par étape', + 'settings.persona.templates.student.label': 'Partenaire d’étude', + 'settings.persona.templates.student.desc': 'Encourageant, pose des questions, langage simple', + 'settings.persona.templates.family.label': 'Assistant familial', + 'settings.persona.templates.family.desc': 'Chaleureux, amical, adapté à tous les âges', 'settings.persona.appearanceHeading': 'Avatar et Voix', 'settings.persona.appearanceDesc': "La couleur de la mascotte, l'avatar personnalisé GIF et la voix de réponse sont configurés dans les paramètres de la mascotte.", diff --git a/app/src/lib/i18n/hi.ts b/app/src/lib/i18n/hi.ts index 3c15779aed..aa9c0de96a 100644 --- a/app/src/lib/i18n/hi.ts +++ b/app/src/lib/i18n/hi.ts @@ -4989,6 +4989,21 @@ const messages: TranslationMap = { 'आपके हाथ से लिखे अन्य अनुभाग सुरक्षित रहते हैं — पूरा पर्सोना देखने के लिए उन्नत पर जाएँ।', 'settings.persona.builder.securityNote': 'तय करना चाहते हैं कि असिस्टेंट क्या कर सकता है?', 'settings.persona.builder.securityLink': 'एजेंट एक्सेस खोलें', + 'settings.persona.templates.heading': 'टेम्पलेट से शुरू करें', + 'settings.persona.templates.desc': + 'एक शुरुआती बिंदु चुनें — यह व्यक्तित्व और संवाद शैली भर देता है। बाद में आप सब कुछ बदल सकते हैं।', + 'settings.persona.templates.doctor.label': 'क्लिनिकल सहायक', + 'settings.persona.templates.doctor.desc': 'सावधान, स्रोत बताता है, अनिश्चितता दर्शाता है', + 'settings.persona.templates.researcher.label': 'शोध सहायक', + 'settings.persona.templates.researcher.desc': 'सटीक, व्यवस्थित, साक्ष्य-आधारित', + 'settings.persona.templates.executive.label': 'कार्यकारी सहायक', + 'settings.persona.templates.executive.desc': 'संक्षिप्त, निर्णायक, कार्य-केंद्रित', + 'settings.persona.templates.teacher.label': 'शिक्षक', + 'settings.persona.templates.teacher.desc': 'धैर्यवान, कदम-दर-कदम समझाता है', + 'settings.persona.templates.student.label': 'अध्ययन साथी', + 'settings.persona.templates.student.desc': 'प्रोत्साहित करता है, सवाल पूछता है, सरल भाषा', + 'settings.persona.templates.family.label': 'पारिवारिक सहायक', + 'settings.persona.templates.family.desc': 'गर्मजोश, मिलनसार, हर उम्र के लिए सुरक्षित', 'settings.persona.appearanceHeading': 'अवतार और आवाज', 'settings.persona.appearanceDesc': 'Mascot रंग, कस्टम GIF अवतार, और उत्तर आवाज Mascot सेटिंग्स में कॉन्फ़िगर किया गया है।', diff --git a/app/src/lib/i18n/id.ts b/app/src/lib/i18n/id.ts index c8c8f5294b..c6cb7d4fcd 100644 --- a/app/src/lib/i18n/id.ts +++ b/app/src/lib/i18n/id.ts @@ -5002,6 +5002,21 @@ const messages: TranslationMap = { 'Bagian lain yang Anda tulis sendiri tetap disimpan — beralih ke Lanjutan untuk melihat persona lengkap.', 'settings.persona.builder.securityNote': 'Ingin memilih apa yang boleh dilakukan asisten?', 'settings.persona.builder.securityLink': 'Buka Akses agen', + 'settings.persona.templates.heading': 'Mulai dari templat', + 'settings.persona.templates.desc': + 'Pilih titik awal — mengisi Kepribadian dan Gaya komunikasi. Anda bisa mengedit semuanya setelahnya.', + 'settings.persona.templates.doctor.label': 'Asisten klinis', + 'settings.persona.templates.doctor.desc': 'Cermat, menyebut sumber, menandai ketidakpastian', + 'settings.persona.templates.researcher.label': 'Asisten riset', + 'settings.persona.templates.researcher.desc': 'Teliti, terstruktur, berbasis bukti', + 'settings.persona.templates.executive.label': 'Asisten eksekutif', + 'settings.persona.templates.executive.desc': 'Ringkas, tegas, berorientasi tindakan', + 'settings.persona.templates.teacher.label': 'Guru', + 'settings.persona.templates.teacher.desc': 'Sabar, menjelaskan langkah demi langkah', + 'settings.persona.templates.student.label': 'Teman belajar', + 'settings.persona.templates.student.desc': 'Menyemangati, memberi kuis, bahasa sederhana', + 'settings.persona.templates.family.label': 'Asisten keluarga', + 'settings.persona.templates.family.desc': 'Hangat, ramah, aman untuk segala usia', 'settings.persona.appearanceHeading': 'Avatar & Suara', 'settings.persona.appearanceDesc': 'Warna Mascot, avatar GIF kustom, dan suara balasan dikonfigurasi dalam pengaturan Mascot.', diff --git a/app/src/lib/i18n/it.ts b/app/src/lib/i18n/it.ts index d0b0509693..ab8e812a85 100644 --- a/app/src/lib/i18n/it.ts +++ b/app/src/lib/i18n/it.ts @@ -5070,6 +5070,21 @@ const messages: TranslationMap = { 'Le altre sezioni che hai scritto a mano vengono mantenute: passa ad Avanzata per vedere la persona completa.', 'settings.persona.builder.securityNote': 'Vuoi scegliere cosa può fare l’assistente?', 'settings.persona.builder.securityLink': 'Apri Accesso agente', + 'settings.persona.templates.heading': 'Parti da un modello', + 'settings.persona.templates.desc': + 'Scegli un punto di partenza: compila Personalità e Stile di comunicazione. Dopo puoi modificare tutto.', + 'settings.persona.templates.doctor.label': 'Assistente clinico', + 'settings.persona.templates.doctor.desc': 'Attento, cita le fonti, segnala l’incertezza', + 'settings.persona.templates.researcher.label': 'Assistente di ricerca', + 'settings.persona.templates.researcher.desc': 'Rigoroso, strutturato, basato sulle prove', + 'settings.persona.templates.executive.label': 'Assistente esecutivo', + 'settings.persona.templates.executive.desc': 'Conciso, deciso, orientato all’azione', + 'settings.persona.templates.teacher.label': 'Insegnante', + 'settings.persona.templates.teacher.desc': 'Paziente, spiega passo dopo passo', + 'settings.persona.templates.student.label': 'Compagno di studio', + 'settings.persona.templates.student.desc': 'Incoraggiante, fa domande, linguaggio semplice', + 'settings.persona.templates.family.label': 'Assistente per la famiglia', + 'settings.persona.templates.family.desc': 'Caloroso, amichevole, adatto a tutte le età', 'settings.persona.appearanceHeading': 'Avatar e Voce', 'settings.persona.appearanceDesc': "Il colore della mascotte, l'avatar personalizzato GIF e la voce di risposta sono configurati nelle impostazioni della mascotte.", diff --git a/app/src/lib/i18n/ko.ts b/app/src/lib/i18n/ko.ts index a439055669..0f8600a218 100644 --- a/app/src/lib/i18n/ko.ts +++ b/app/src/lib/i18n/ko.ts @@ -4937,6 +4937,21 @@ const messages: TranslationMap = { '직접 작성한 다른 섹션은 그대로 유지됩니다 — 전체 페르소나를 보려면 고급으로 전환하세요.', 'settings.persona.builder.securityNote': '어시스턴트가 할 수 있는 일을 정하시겠어요?', 'settings.persona.builder.securityLink': '에이전트 액세스 열기', + 'settings.persona.templates.heading': '템플릿으로 시작', + 'settings.persona.templates.desc': + '시작점을 고르세요 — 성격과 커뮤니케이션 스타일을 채워 줍니다. 이후 모두 수정할 수 있습니다.', + 'settings.persona.templates.doctor.label': '임상 어시스턴트', + 'settings.persona.templates.doctor.desc': '신중하고 출처를 밝히며 불확실성을 표시', + 'settings.persona.templates.researcher.label': '리서치 어시스턴트', + 'settings.persona.templates.researcher.desc': '엄밀하고 체계적이며 근거 우선', + 'settings.persona.templates.executive.label': '임원 어시스턴트', + 'settings.persona.templates.executive.desc': '간결하고 결단력 있으며 실행 중심', + 'settings.persona.templates.teacher.label': '교사', + 'settings.persona.templates.teacher.desc': '인내심 있게 단계별로 설명', + 'settings.persona.templates.student.label': '공부 친구', + 'settings.persona.templates.student.desc': '격려하고 퀴즈를 내며 쉬운 언어 사용', + 'settings.persona.templates.family.label': '가족 어시스턴트', + 'settings.persona.templates.family.desc': '따뜻하고 친근하며 모든 연령에 안전', 'settings.persona.appearanceHeading': '아바타 및 음성', 'settings.persona.appearanceDesc': '마스코트 색상, 사용자 지정 GIF 아바타, 응답 음성은 마스코트 설정에서 구성합니다.', diff --git a/app/src/lib/i18n/pl.ts b/app/src/lib/i18n/pl.ts index 2450ee1997..dbcb3ad87d 100644 --- a/app/src/lib/i18n/pl.ts +++ b/app/src/lib/i18n/pl.ts @@ -5056,6 +5056,21 @@ const messages: TranslationMap = { 'Wszelkie inne sekcje napisane ręcznie zostają zachowane — przełącz na Zaawansowany, aby zobaczyć pełną personę.', 'settings.persona.builder.securityNote': 'Chcesz wybrać, co asystent może robić?', 'settings.persona.builder.securityLink': 'Otwórz Dostęp agenta', + 'settings.persona.templates.heading': 'Zacznij od szablonu', + 'settings.persona.templates.desc': + 'Wybierz punkt wyjścia — wypełni Osobowość i Styl komunikacji. Wszystko możesz później edytować.', + 'settings.persona.templates.doctor.label': 'Asystent kliniczny', + 'settings.persona.templates.doctor.desc': 'Uważny, podaje źródła, sygnalizuje niepewność', + 'settings.persona.templates.researcher.label': 'Asystent badawczy', + 'settings.persona.templates.researcher.desc': 'Rzetelny, uporządkowany, oparty na dowodach', + 'settings.persona.templates.executive.label': 'Asystent kierownictwa', + 'settings.persona.templates.executive.desc': 'Zwięzły, zdecydowany, nastawiony na działanie', + 'settings.persona.templates.teacher.label': 'Nauczyciel', + 'settings.persona.templates.teacher.desc': 'Cierpliwy, tłumaczy krok po kroku', + 'settings.persona.templates.student.label': 'Partner do nauki', + 'settings.persona.templates.student.desc': 'Zachęcający, zadaje pytania, prosty język', + 'settings.persona.templates.family.label': 'Asystent rodzinny', + 'settings.persona.templates.family.desc': 'Ciepły, przyjazny, bezpieczny dla każdego wieku', 'settings.persona.appearanceHeading': 'Awatar i głos', 'settings.persona.appearanceDesc': 'Kolor maskotki, własny awatar GIF i głos odpowiedzi są konfigurowane w ustawieniach Maskotki.', diff --git a/app/src/lib/i18n/pt.ts b/app/src/lib/i18n/pt.ts index ceabfff91a..e36145982f 100644 --- a/app/src/lib/i18n/pt.ts +++ b/app/src/lib/i18n/pt.ts @@ -5072,6 +5072,21 @@ const messages: TranslationMap = { 'Quaisquer outras seções que você escreveu à mão são mantidas — mude para Avançado para ver a persona completa.', 'settings.persona.builder.securityNote': 'Quer escolher o que o assistente pode fazer?', 'settings.persona.builder.securityLink': 'Abrir Acesso do agente', + 'settings.persona.templates.heading': 'Começar a partir de um modelo', + 'settings.persona.templates.desc': + 'Escolha um ponto de partida: preenche Personalidade e Estilo de comunicação. Depois você pode editar tudo.', + 'settings.persona.templates.doctor.label': 'Assistente clínico', + 'settings.persona.templates.doctor.desc': 'Cuidadoso, cita fontes, sinaliza incerteza', + 'settings.persona.templates.researcher.label': 'Assistente de pesquisa', + 'settings.persona.templates.researcher.desc': 'Rigoroso, estruturado, baseado em evidências', + 'settings.persona.templates.executive.label': 'Assistente executivo', + 'settings.persona.templates.executive.desc': 'Conciso, decidido, orientado à ação', + 'settings.persona.templates.teacher.label': 'Professor', + 'settings.persona.templates.teacher.desc': 'Paciente, explica passo a passo', + 'settings.persona.templates.student.label': 'Parceiro de estudos', + 'settings.persona.templates.student.desc': 'Encorajador, faz perguntas, linguagem simples', + 'settings.persona.templates.family.label': 'Assistente da família', + 'settings.persona.templates.family.desc': 'Acolhedor, amigável, seguro para todas as idades', 'settings.persona.appearanceHeading': 'Avatar e Voz', 'settings.persona.appearanceDesc': 'A cor do mascote, o avatar personalizado GIF e a voz de resposta são configurados nas configurações do mascote.', diff --git a/app/src/lib/i18n/ru.ts b/app/src/lib/i18n/ru.ts index 598fcfafdb..9dd82733e4 100644 --- a/app/src/lib/i18n/ru.ts +++ b/app/src/lib/i18n/ru.ts @@ -5030,6 +5030,23 @@ const messages: TranslationMap = { 'Все другие разделы, написанные вручную, сохраняются — переключитесь на «Расширенный», чтобы увидеть персону целиком.', 'settings.persona.builder.securityNote': 'Хотите выбрать, что разрешено ассистенту?', 'settings.persona.builder.securityLink': 'Открыть доступ агента', + 'settings.persona.templates.heading': 'Начать с шаблона', + 'settings.persona.templates.desc': + 'Выберите отправную точку — она заполнит «Характер» и «Стиль общения». Потом всё можно изменить.', + 'settings.persona.templates.doctor.label': 'Клинический ассистент', + 'settings.persona.templates.doctor.desc': + 'Внимателен, ссылается на источники, отмечает неопределённость', + 'settings.persona.templates.researcher.label': 'Научный ассистент', + 'settings.persona.templates.researcher.desc': + 'Строгий, структурированный, опирается на доказательства', + 'settings.persona.templates.executive.label': 'Ассистент руководителя', + 'settings.persona.templates.executive.desc': 'Краткий, решительный, ориентирован на действие', + 'settings.persona.templates.teacher.label': 'Учитель', + 'settings.persona.templates.teacher.desc': 'Терпеливый, объясняет шаг за шагом', + 'settings.persona.templates.student.label': 'Помощник в учёбе', + 'settings.persona.templates.student.desc': 'Поддерживающий, задаёт вопросы, простой язык', + 'settings.persona.templates.family.label': 'Семейный ассистент', + 'settings.persona.templates.family.desc': 'Тёплый, дружелюбный, подходит для всех возрастов', 'settings.persona.appearanceHeading': 'Аватар и голос', 'settings.persona.appearanceDesc': 'Цвет талисмана, пользовательский аватар GIF и голос ответа настраиваются в настройках талисмана.', diff --git a/app/src/lib/i18n/zh-CN.ts b/app/src/lib/i18n/zh-CN.ts index 39cdad3bf3..d73b15814d 100644 --- a/app/src/lib/i18n/zh-CN.ts +++ b/app/src/lib/i18n/zh-CN.ts @@ -4738,6 +4738,21 @@ const messages: TranslationMap = { '你手写的其他部分都会保留——切换到「高级」可查看完整角色设定。', 'settings.persona.builder.securityNote': '想选择助手可以做什么?', 'settings.persona.builder.securityLink': '打开代理访问权限', + 'settings.persona.templates.heading': '从模板开始', + 'settings.persona.templates.desc': + '选择一个起点——它会填写「性格」和「沟通风格」。之后你可以随意修改。', + 'settings.persona.templates.doctor.label': '临床助手', + 'settings.persona.templates.doctor.desc': '严谨、引用来源、标注不确定', + 'settings.persona.templates.researcher.label': '研究助手', + 'settings.persona.templates.researcher.desc': '严谨、有条理、以证据为先', + 'settings.persona.templates.executive.label': '高管助手', + 'settings.persona.templates.executive.desc': '简洁、果断、注重行动', + 'settings.persona.templates.teacher.label': '老师', + 'settings.persona.templates.teacher.desc': '有耐心、循序渐进地讲解', + 'settings.persona.templates.student.label': '学习搭子', + 'settings.persona.templates.student.desc': '鼓励式、出小测、通俗易懂', + 'settings.persona.templates.family.label': '家庭助手', + 'settings.persona.templates.family.desc': '温暖、友好、老少皆宜', 'settings.persona.appearanceHeading': '头像和声音', 'settings.persona.appearanceDesc': '吉祥物颜色、自定义 GIF 头像和回复声音在吉祥物设置中配置。', 'settings.persona.openMascotSettings': '打开吉祥物设置',