privateboard 0.1.36 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ <svg viewBox="120 128 272 272" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges" preserveAspectRatio="xMidYMid meet"><rect x="192" y="160" width="16" height="16" fill="#ce8a5a"/><rect x="208" y="160" width="16" height="16" fill="#ce8a5a"/><rect x="224" y="160" width="16" height="16" fill="#a35a32"/><rect x="240" y="160" width="16" height="16" fill="#a35a32"/><rect x="256" y="160" width="16" height="16" fill="#a35a32"/><rect x="272" y="160" width="16" height="16" fill="#a35a32"/><rect x="288" y="160" width="16" height="16" fill="#a35a32"/><rect x="304" y="160" width="16" height="16" fill="#a35a32"/><rect x="176" y="176" width="16" height="16" fill="#ce8a5a"/><rect x="192" y="176" width="16" height="16" fill="#ce8a5a"/><rect x="208" y="176" width="16" height="16" fill="#a35a32"/><rect x="224" y="176" width="16" height="16" fill="#a35a32"/><rect x="240" y="176" width="16" height="16" fill="#a35a32"/><rect x="256" y="176" width="16" height="16" fill="#a35a32"/><rect x="272" y="176" width="16" height="16" fill="#a35a32"/><rect x="288" y="176" width="16" height="16" fill="#a35a32"/><rect x="304" y="176" width="16" height="16" fill="#a35a32"/><rect x="320" y="176" width="16" height="16" fill="#a35a32"/><rect x="160" y="192" width="16" height="16" fill="#ce8a5a"/><rect x="176" y="192" width="16" height="16" fill="#ce8a5a"/><rect x="192" y="192" width="16" height="16" fill="#a35a32"/><rect x="208" y="192" width="16" height="16" fill="#a35a32"/><rect x="224" y="192" width="16" height="16" fill="#a35a32"/><rect x="240" y="192" width="16" height="16" fill="#a35a32"/><rect x="256" y="192" width="16" height="16" fill="#a35a32"/><rect x="272" y="192" width="16" height="16" fill="#a35a32"/><rect x="288" y="192" width="16" height="16" fill="#a35a32"/><rect x="304" y="192" width="16" height="16" fill="#a35a32"/><rect x="320" y="192" width="16" height="16" fill="#a35a32"/><rect x="336" y="192" width="16" height="16" fill="#a35a32"/><rect x="160" y="208" width="16" height="16" fill="#a35a32"/><rect x="176" y="208" width="16" height="16" fill="#a35a32"/><rect x="192" y="208" width="16" height="16" fill="#a35a32"/><rect x="208" y="208" width="16" height="16" fill="#a35a32"/><rect x="224" y="208" width="16" height="16" fill="#a35a32"/><rect x="240" y="208" width="16" height="16" fill="#a35a32"/><rect x="256" y="208" width="16" height="16" fill="#a35a32"/><rect x="272" y="208" width="16" height="16" fill="#a35a32"/><rect x="288" y="208" width="16" height="16" fill="#a35a32"/><rect x="304" y="208" width="16" height="16" fill="#a35a32"/><rect x="320" y="208" width="16" height="16" fill="#a35a32"/><rect x="336" y="208" width="16" height="16" fill="#a35a32"/><rect x="160" y="224" width="16" height="16" fill="#a35a32"/><rect x="176" y="224" width="16" height="16" fill="#a35a32"/><rect x="192" y="224" width="16" height="16" fill="#a35a32"/><rect x="208" y="224" width="16" height="16" fill="#1a0805"/><rect x="224" y="224" width="16" height="16" fill="#a35a32"/><rect x="240" y="224" width="16" height="16" fill="#a35a32"/><rect x="256" y="224" width="16" height="16" fill="#a35a32"/><rect x="272" y="224" width="16" height="16" fill="#a35a32"/><rect x="288" y="224" width="16" height="16" fill="#1a0805"/><rect x="304" y="224" width="16" height="16" fill="#a35a32"/><rect x="320" y="224" width="16" height="16" fill="#a35a32"/><rect x="336" y="224" width="16" height="16" fill="#a35a32"/><rect x="160" y="240" width="16" height="16" fill="#a35a32"/><rect x="176" y="240" width="16" height="16" fill="#a35a32"/><rect x="192" y="240" width="16" height="16" fill="#a35a32"/><rect x="208" y="240" width="16" height="16" fill="#a35a32"/><rect x="224" y="240" width="16" height="16" fill="#a35a32"/><rect x="240" y="240" width="16" height="16" fill="#a35a32"/><rect x="256" y="240" width="16" height="16" fill="#a35a32"/><rect x="272" y="240" width="16" height="16" fill="#a35a32"/><rect x="288" y="240" width="16" height="16" fill="#a35a32"/><rect x="304" y="240" width="16" height="16" fill="#a35a32"/><rect x="320" y="240" width="16" height="16" fill="#a35a32"/><rect x="336" y="240" width="16" height="16" fill="#a35a32"/><rect x="160" y="256" width="16" height="16" fill="#a35a32"/><rect x="176" y="256" width="16" height="16" fill="#a35a32"/><rect x="192" y="256" width="16" height="16" fill="#a35a32"/><rect x="208" y="256" width="16" height="16" fill="#a35a32"/><rect x="224" y="256" width="16" height="16" fill="#a35a32"/><rect x="240" y="256" width="16" height="16" fill="#a35a32"/><rect x="256" y="256" width="16" height="16" fill="#a35a32"/><rect x="272" y="256" width="16" height="16" fill="#a35a32"/><rect x="288" y="256" width="16" height="16" fill="#a35a32"/><rect x="304" y="256" width="16" height="16" fill="#a35a32"/><rect x="320" y="256" width="16" height="16" fill="#a35a32"/><rect x="336" y="256" width="16" height="16" fill="#a35a32"/><rect x="160" y="272" width="16" height="16" fill="#5a2a10"/><rect x="176" y="272" width="16" height="16" fill="#5a2a10"/><rect x="192" y="272" width="16" height="16" fill="#5a2a10"/><rect x="208" y="272" width="16" height="16" fill="#5a2a10"/><rect x="224" y="272" width="16" height="16" fill="#5a2a10"/><rect x="240" y="272" width="16" height="16" fill="#cfb56a"/><rect x="256" y="272" width="16" height="16" fill="#cfb56a"/><rect x="272" y="272" width="16" height="16" fill="#5a2a10"/><rect x="288" y="272" width="16" height="16" fill="#5a2a10"/><rect x="304" y="272" width="16" height="16" fill="#5a2a10"/><rect x="320" y="272" width="16" height="16" fill="#5a2a10"/><rect x="336" y="272" width="16" height="16" fill="#5a2a10"/><rect x="160" y="288" width="16" height="16" fill="#a35a32"/><rect x="176" y="288" width="16" height="16" fill="#a35a32"/><rect x="192" y="288" width="16" height="16" fill="#a35a32"/><rect x="208" y="288" width="16" height="16" fill="#a35a32"/><rect x="224" y="288" width="16" height="16" fill="#a35a32"/><rect x="240" y="288" width="16" height="16" fill="#a35a32"/><rect x="256" y="288" width="16" height="16" fill="#a35a32"/><rect x="272" y="288" width="16" height="16" fill="#a35a32"/><rect x="288" y="288" width="16" height="16" fill="#a35a32"/><rect x="304" y="288" width="16" height="16" fill="#a35a32"/><rect x="320" y="288" width="16" height="16" fill="#6e3814"/><rect x="336" y="288" width="16" height="16" fill="#6e3814"/><rect x="160" y="304" width="16" height="16" fill="#a35a32"/><rect x="176" y="304" width="16" height="16" fill="#a35a32"/><rect x="192" y="304" width="16" height="16" fill="#a35a32"/><rect x="208" y="304" width="16" height="16" fill="#a35a32"/><rect x="224" y="304" width="16" height="16" fill="#a35a32"/><rect x="240" y="304" width="16" height="16" fill="#a35a32"/><rect x="256" y="304" width="16" height="16" fill="#a35a32"/><rect x="272" y="304" width="16" height="16" fill="#a35a32"/><rect x="288" y="304" width="16" height="16" fill="#a35a32"/><rect x="304" y="304" width="16" height="16" fill="#6e3814"/><rect x="320" y="304" width="16" height="16" fill="#6e3814"/><rect x="336" y="304" width="16" height="16" fill="#a35a32"/><rect x="176" y="320" width="16" height="16" fill="#a35a32"/><rect x="192" y="320" width="16" height="16" fill="#a35a32"/><rect x="208" y="320" width="16" height="16" fill="#a35a32"/><rect x="224" y="320" width="16" height="16" fill="#a35a32"/><rect x="240" y="320" width="16" height="16" fill="#a35a32"/><rect x="256" y="320" width="16" height="16" fill="#a35a32"/><rect x="272" y="320" width="16" height="16" fill="#a35a32"/><rect x="288" y="320" width="16" height="16" fill="#a35a32"/><rect x="304" y="320" width="16" height="16" fill="#a35a32"/><rect x="320" y="320" width="16" height="16" fill="#6e3814"/><rect x="192" y="336" width="16" height="16" fill="#a35a32"/><rect x="208" y="336" width="16" height="16" fill="#a35a32"/><rect x="288" y="336" width="16" height="16" fill="#a35a32"/><rect x="304" y="336" width="16" height="16" fill="#6e3814"/><rect x="192" y="352" width="16" height="16" fill="#3e1c08"/><rect x="208" y="352" width="16" height="16" fill="#3e1c08"/><rect x="288" y="352" width="16" height="16" fill="#3e1c08"/><rect x="304" y="352" width="16" height="16" fill="#3e1c08"/></svg>
@@ -90,6 +90,12 @@
90
90
  elevationDeg: 24, // soft top-down lean (default 30 was steep, 18 too flat)
91
91
  lookAtY: 0.75, // target slightly above the table top so seated heads centre
92
92
  },
93
+ // Suppress the built-in loading veil · the marketing hero
94
+ // already shows a static poster (`home-3d-poster.*`) which
95
+ // covers the mount → first-frame gap. Layering the veil on
96
+ // top of the poster would just darken it before the canvas
97
+ // takes over.
98
+ loading: false,
93
99
  })) return;
94
100
 
95
101
  // Pull the cast + start the rotating-speaker driver. The mock
package/public/home.html CHANGED
@@ -659,8 +659,8 @@
659
659
  height: 64px;
660
660
  image-rendering: pixelated;
661
661
  image-rendering: crisp-edges;
662
- background: var(--panel-2);
663
- border: 0.5px solid var(--line);
662
+ background: transparent;
663
+ border: 0;
664
664
  flex-shrink: 0;
665
665
  margin-bottom: 4px;
666
666
  }
@@ -1622,7 +1622,7 @@
1622
1622
 
1623
1623
  <div class="hero-actions">
1624
1624
  <a href="#install" class="big-btn primary">[ ◆ Convene a Room ]</a>
1625
- <a href="https://github.com/kaysaith1900/privateboard/releases/download/v0.1.34/PrivateBoard-0.1.34-arm64.dmg"
1625
+ <a href="https://github.com/kaysaith1900/privateboard/releases/download/v0.1.37/PrivateBoard-0.1.37-arm64.dmg"
1626
1626
  class="big-btn secondary"
1627
1627
  download
1628
1628
  aria-label="Download PrivateBoard for macOS (Apple Silicon)"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M12 3v12"/><polyline points="6 11 12 17 18 11"/><path d="M5 21h14"/></svg> Download for Mac</a>
package/public/i18n.js CHANGED
@@ -386,6 +386,74 @@
386
386
  ap_voice_locked_title: "Configure a voice provider to let this director speak with their own register.",
387
387
  ap_voice_locked_body: "Configure a voice provider to unlock TTS — your director will be able to speak in their own register, with adjustable pitch, speed and emotion.",
388
388
  ap_voice_locked_cta: "Unlock voice",
389
+ // ── Voice cloning ───────────────────────────────────────────
390
+ voice_clone_btn: "FORGE VOICE",
391
+ voice_clone_btn_kicker: "// VOICE · CLONE-FORGE",
392
+ voice_clone_btn_hint: "Train a new voice for this director from an audio or video sample.",
393
+ voice_clone_kicker: "// CLONE",
394
+ voice_clone_classification_left: "// VOICE CLONE · CLASSIFIED",
395
+ voice_clone_classification_right: "private board",
396
+ voice_clone_head_meta: "// CLONE · ACTIVE",
397
+ voice_clone_modal_title: "Clone voice · {name}",
398
+ voice_clone_source_label: "Voice source",
399
+ voice_clone_trim_decoding: "Decoding audio…",
400
+ voice_clone_trim_unsupported: "Can't preview this format; the file will be uploaded as-is.",
401
+ voice_clone_trim_video_unsupported: "Couldn't extract audio from this video. Re-export it as mp4/mov (AAC) or convert to a plain audio file (mp3 / m4a / wav) and try again.",
402
+ voice_clone_trim_selected: "· {sel} of {total}",
403
+ voice_clone_trimming: "Trimming selection…",
404
+ voice_clone_file_pick: "Choose an audio or video file",
405
+ voice_clone_file_hint: "Audio: mp3 / m4a / wav · Video: mp4 / mov / webm — we'll pull the audio track in your browser.",
406
+ voice_clone_mode_upload: "Upload file",
407
+ voice_clone_mode_record: "Record voice",
408
+ voice_clone_record_script: "Read this aloud — it covers a wide phoneme range so the clone captures your timbre well:",
409
+ voice_clone_record_script_text: "The quick brown fox jumps over the lazy dog. She sells seashells by the seashore. How vexingly quick daft zebras jump! Sphinx of black quartz, judge my vow.",
410
+ voice_clone_record_start: "Start recording",
411
+ voice_clone_record_idle: "Tap to record",
412
+ voice_clone_record_recording: "Recording… tap to stop",
413
+ voice_clone_record_ready: "Recording ready. Confirm to clone.",
414
+ voice_clone_record_empty: "No audio captured. Try again.",
415
+ voice_clone_record_play: "Play back",
416
+ voice_clone_record_redo: "Re-record",
417
+ voice_clone_record_mic_err: "Microphone access denied: {msg}",
418
+ voice_clone_record_init_err: "Recorder init failed: {msg}",
419
+ voice_clone_label_label: "Label · required",
420
+ voice_clone_label_placeholder: "Name this cloned voice (e.g. Chloe)",
421
+ voice_clone_minimax_group_label: "MiniMax Group ID",
422
+ voice_clone_minimax_group_placeholder: "e.g. 1838xxxxxx · required if your key isn't a JWT",
423
+ voice_clone_hint: "Use a clean 10s–3min sample of the target voice. <strong>Only one clone runs at a time.</strong>",
424
+ voice_clone_confirm: "Start cloning",
425
+ voice_clone_dismiss: "Cancel",
426
+ voice_clone_cancel: "Cancel clone",
427
+ voice_clone_minimize: "Minimize",
428
+ voice_clone_minimize_btn: "Run in background",
429
+ voice_clone_close: "Close",
430
+ voice_clone_stage_fetch: "Fetch audio",
431
+ voice_clone_stage_upload: "Upload to provider",
432
+ voice_clone_stage_train: "Wait for clone",
433
+ voice_clone_uploading_file: "Uploading file…",
434
+ voice_clone_success: "Voice cloned and applied to the director.",
435
+ voice_clone_failed: "Clone failed.",
436
+ voice_clone_retry: "Retry",
437
+ voice_clone_done_btn: "Done",
438
+ voice_clone_apply_close_btn: "Apply and close",
439
+ voice_clone_success_kicker: "// CLONED",
440
+ voice_clone_success_title: "Cloned · {name}",
441
+ voice_clone_preview_default_name: "this voice",
442
+ voice_clone_preview_default_text: "I'm {name}, a member of your private boardroom. Looking forward to working with you.",
443
+ voice_clone_preview_sample_placeholder: "Sample line for preview",
444
+ voice_clone_preview_btn_aria: "Preview cloned voice",
445
+ voice_clone_preview_hint: "Edit the line above, then tap the play button to hear the cloned voice.",
446
+ voice_clone_preview_err: "Preview failed: {msg}",
447
+ voice_clone_preview_audio_err: "Browser couldn't decode the audio. Try a different sample line.",
448
+ voice_clone_preview_missing_voice: "No voice_id captured — re-open the clone modal.",
449
+ voice_clone_pill_label_short: "Cloning",
450
+ voice_clone_pill_done: "Cloned",
451
+ voice_clone_pill_failed: "Clone failed",
452
+ voice_clone_pill_aria: "Open voice cloning panel",
453
+ voice_clone_err_provider_auth: "The provider rejected the API key. Re-check it in voice settings.",
454
+ voice_clone_err_provider_quota: "Provider balance or subscription doesn't allow cloning.",
455
+ voice_clone_err_audio_too_short: "Audio sample is too short to clone from.",
456
+ voice_clone_err_audio_too_long: "Audio sample is too long.",
389
457
  ap_voice_locked_providers: "supports <b>MiniMax</b> · <b>ElevenLabs</b>",
390
458
  ap_voice_modify_pitch: "pitch trim",
391
459
  ap_voice_modify_intensity: "intensity trim",
@@ -397,6 +465,15 @@
397
465
  short (under 200 chars · server caps anyway) and self-
398
466
  describing so the voice's character lands quickly. */
399
467
  ap_voice_preview_sample: "Hello, I'm a member of your boardroom. Looking forward to working with you.",
468
+ ap_voice_preview_text_label: "Preview line",
469
+ ap_voice_section_voice: "Voice",
470
+ ap_voice_section_emotion: "Emotion",
471
+ ap_voice_section_preview: "Preview line",
472
+ ap_voice_group_cloned_provider: "// cloned · {provider}",
473
+ ap_voice_rename_btn: "Rename voice",
474
+ ap_voice_rename_prompt: "Display name for voice {id}",
475
+ ap_voice_rename_clear_confirm: "Clear the custom name and fall back to the provider's label?",
476
+ ap_voice_rename_err: "Rename failed: {msg}",
400
477
  /* Voice catalogue fetch errors surfaced inside the picker · the
401
478
  backend returns a structured `error` field with a `code` and
402
479
  optional `fixUrl`; these keys form the human-readable banner. */
@@ -462,6 +539,35 @@
462
539
 
463
540
  convene_show_more: "Show more ↓",
464
541
  convene_show_less: "Show less ↑",
542
+ // Thread feature · private 1:1 director aside (Slack-DM-style float)
543
+ thread_trigger: "// thread",
544
+ thread_trigger_short: "Reply privately",
545
+ thread_trigger_label: "Thread",
546
+ thread_trigger_tip: "Pull {name} aside privately",
547
+ thread_head_kicker: "private with you",
548
+ thread_minimize: "—",
549
+ thread_minimize_tip: "Minimize",
550
+ thread_close: "✕",
551
+ thread_close_tip: "Close",
552
+ thread_empty_title: "Pull them aside",
553
+ thread_empty_deck: "What does {name} know that you want to dig into? They have the full room context up to this point.",
554
+ thread_composer_placeholder: "Ask {name} anything…",
555
+ thread_send: "Send",
556
+ thread_stop: "Stop",
557
+ thread_stop_tip: "Interrupt the director's reply",
558
+ thread_maximize: "⛶",
559
+ thread_maximize_tip: "Maximize to side panel",
560
+ thread_restore: "⊟",
561
+ thread_restore_tip: "Restore as floating window",
562
+ thread_dock_resize_tip: "Drag to resize panel",
563
+ head_threads_tip: "All private threads in this room",
564
+ head_threads_label: "All threads",
565
+ threads_list_title: "// Private threads",
566
+ threads_list_loading: "Loading…",
567
+ threads_list_empty: "No private threads yet · open one from the // Thread button on any director's message.",
568
+ threads_list_active: "open",
569
+ threads_list_delete_tip: "Delete this thread",
570
+ threads_list_delete_confirm: "Delete the private thread with {name}? This can't be undone.",
465
571
  convene_eyebrow: "▸ Convene · Initial Question",
466
572
  convene_meta_to: "to",
467
573
 
@@ -481,6 +587,7 @@
481
587
  msg_ws_query_label: "query",
482
588
  msg_ws_expand_hide: "Collapse",
483
589
  msg_ws_secs: "{n}s",
590
+ msg_ws_card_body: "Searched \"{query}\"",
484
591
  msg_chair_direct_kicker: "▸ chair · responding to you",
485
592
  msg_chair_intervention_kicker: "▸ chair note",
486
593
  msg_chair_billing_kicker: "▸ billing · attention needed",
@@ -594,6 +701,20 @@
594
701
  rec_stop_cancel_mark: "↩ Cancel",
595
702
  rec_stop_cancel_deck: "Stay here · the recording keeps running.",
596
703
  rec_saved_toast: "Recording saved · file downloaded",
704
+ // Pause-after-current save modal · the room reached soft-pause
705
+ // while a recording was still capturing. Ask the user what to
706
+ // do with the in-flight clip before it starts recording silence.
707
+ rec_pause_save_class: "pause-after-current · save?",
708
+ rec_pause_save_right: "// in-flight recording",
709
+ rec_pause_save_tag: "▸ recording",
710
+ rec_pause_save_title: "Save the recording?",
711
+ rec_pause_save_deck: "The current director just finished and the room is paused. Your recording is still rolling — pick how to handle it.",
712
+ rec_pause_save_adjourn_mark: "✓ Save & adjourn room",
713
+ rec_pause_save_adjourn_deck: "Stop the recording, download the file, then end the room.",
714
+ rec_pause_save_keep_mark: "✓ Save & keep paused",
715
+ rec_pause_save_keep_deck: "Stop the recording and download. Room stays paused so you can resume later.",
716
+ rec_pause_save_continue_mark: "↩ Keep recording",
717
+ rec_pause_save_continue_deck: "Stay paused; the recording keeps running (warning: it'll capture silence).",
597
718
  // Resume-choice modal · shown when user clicks Resume on a
598
719
  // voice room with no voice key configured. Same 3-button
599
720
  // shape as the pause / reload modals.
@@ -1199,6 +1320,8 @@ When the room ___, raise an objection.`,
1199
1320
  us_active_llm_save_btn: "Save & activate",
1200
1321
  us_active_llm_save_failed: "Could not save the credential — check the key and try again.",
1201
1322
  us_active_llm_delete_active_confirm: "Removing the active provider — the boardroom will fall back to another added provider, or stop working until you add a new one. Continue?",
1323
+ us_llm_delete_confirm: "Remove this LLM credential? This can't be undone.",
1324
+ us_provider_key_delete_confirm: "Remove this {label} API key? This can't be undone.",
1202
1325
  us_active_llm_switch_confirm: "Switch active LLM provider to {label}? Every director will be reassigned to the new provider's models.",
1203
1326
  voice_cred_section_title: "▸ Voice provider",
1204
1327
  voice_cred_section_deck: "Powers spoken meetings · directors pull voices from the active provider's catalog only.",
@@ -1210,6 +1333,7 @@ When the room ___, raise an objection.`,
1210
1333
  voice_cred_picker_title: "Choose a voice provider",
1211
1334
  voice_cred_switch_confirm: "Switch active voice provider to {label}? Each director's voice will be reshuffled from the new provider's catalog.",
1212
1335
  voice_cred_delete_confirm_active: "Remove the active voice credential? Director voices reshuffle from the next available credential.",
1336
+ voice_cred_delete_confirm: "Remove this voice credential? This can't be undone.",
1213
1337
  voice_cred_save_failed: "Could not save the voice credential — check the key and try again.",
1214
1338
  ap_voice_empty_no_active: "No active voice provider · open Settings to add one",
1215
1339
  search_cred_section_title: "▸ Search provider",
@@ -1222,6 +1346,7 @@ When the room ___, raise an objection.`,
1222
1346
  search_cred_picker_title: "Choose a search provider",
1223
1347
  search_cred_switch_confirm: "Switch active search provider to {label}? Web Search routes through this credential from now on.",
1224
1348
  search_cred_delete_confirm_active: "Remove the active search credential? Web Search rotates to the next available credential, or stops working until you add one.",
1349
+ search_cred_delete_confirm: "Remove this search credential? This can't be undone.",
1225
1350
  search_cred_save_failed: "Could not save the search credential — check the key and try again.",
1226
1351
  us_ws_backend_tag: "WEB SEARCH BACKEND",
1227
1352
  us_ws_backend_deck:
@@ -1643,12 +1768,89 @@ When the room ___, raise an objection.`,
1643
1768
  ap_voice_locked_title: "配置一个语音服务商,董事就能以自己的音色发声。",
1644
1769
  ap_voice_locked_body: "配置一个语音服务商即可解锁 TTS — 董事将以自己的音色发声,可调语速、语调与情绪。",
1645
1770
  ap_voice_locked_cta: "解锁声音",
1771
+ // ── 音色克隆 ───────────────────────────────────────────
1772
+ voice_clone_btn: "锻造音色",
1773
+ voice_clone_btn_kicker: "// 音色 · 克隆熔炉",
1774
+ voice_clone_btn_hint: "上传音频或视频样本,为这位董事打造专属新音色。",
1775
+ voice_clone_kicker: "// 音色克隆",
1776
+ voice_clone_classification_left: "// 音色克隆 · 机密",
1777
+ voice_clone_classification_right: "私董会",
1778
+ voice_clone_head_meta: "// 克隆 · 进行中",
1779
+ voice_clone_modal_title: "克隆音色 · {name}",
1780
+ voice_clone_source_label: "音色来源",
1781
+ voice_clone_trim_decoding: "正在解码音频…",
1782
+ voice_clone_trim_unsupported: "无法预览这个格式,文件将原样上传。",
1783
+ voice_clone_trim_video_unsupported: "无法从这段视频中提取音频。请改用 mp4/mov(AAC 编码)重新导出,或转成纯音频文件 (mp3 / m4a / wav) 后再试。",
1784
+ voice_clone_trim_selected: "· {sel} / {total}",
1785
+ voice_clone_trimming: "正在裁剪选段…",
1786
+ voice_clone_file_pick: "选择音频或视频文件",
1787
+ voice_clone_file_hint: "音频:mp3 / m4a / wav · 视频:mp4 / mov / webm — 浏览器会在本地从视频里抽取音轨。",
1788
+ voice_clone_mode_upload: "上传文件",
1789
+ voice_clone_mode_record: "录制声音",
1790
+ voice_clone_record_script: "请朗读以下文字 — 它覆盖了较全的音素,能帮助克隆更准确地捕捉你的音色:",
1791
+ voice_clone_record_script_text: "今天天气真好,阳光透过窗户洒在书桌上。请你说说话,让我们听一下你的声音特点。从北京到上海,从清晨到傍晚,每一个故事都值得被认真聆听。",
1792
+ voice_clone_record_start: "开始录制",
1793
+ voice_clone_record_idle: "点击开始录制",
1794
+ voice_clone_record_recording: "录制中… 点击停止",
1795
+ voice_clone_record_ready: "录制完成,可以确认克隆",
1796
+ voice_clone_record_empty: "未捕捉到音频,请重试",
1797
+ voice_clone_record_play: "试听",
1798
+ voice_clone_record_redo: "重录",
1799
+ voice_clone_record_mic_err: "麦克风权限被拒绝: {msg}",
1800
+ voice_clone_record_init_err: "录音初始化失败: {msg}",
1801
+ voice_clone_label_label: "标签 · 必填",
1802
+ voice_clone_label_placeholder: "给这个克隆音色起个名字 (例如 Chloe)",
1803
+ voice_clone_minimax_group_label: "MiniMax Group ID",
1804
+ voice_clone_minimax_group_placeholder: "例如 1838xxxxxx · 当 key 不是 JWT 时必填",
1805
+ voice_clone_hint: "请使用一段干净的 10 秒到 3 分钟目标音色样本。<strong>同一时间只能克隆一个音色。</strong>",
1806
+ voice_clone_confirm: "开始克隆",
1807
+ voice_clone_dismiss: "取消",
1808
+ voice_clone_cancel: "取消克隆",
1809
+ voice_clone_minimize: "最小化",
1810
+ voice_clone_minimize_btn: "在后台继续",
1811
+ voice_clone_close: "关闭",
1812
+ voice_clone_stage_fetch: "抓取音频",
1813
+ voice_clone_stage_upload: "上传到服务商",
1814
+ voice_clone_stage_train: "等待克隆完成",
1815
+ voice_clone_uploading_file: "正在上传文件…",
1816
+ voice_clone_success: "音色已克隆,已应用到该董事。",
1817
+ voice_clone_failed: "克隆失败。",
1818
+ voice_clone_retry: "重试",
1819
+ voice_clone_done_btn: "完成",
1820
+ voice_clone_apply_close_btn: "应用并关闭",
1821
+ voice_clone_success_kicker: "// 克隆完成",
1822
+ voice_clone_success_title: "已克隆 · {name}",
1823
+ voice_clone_preview_default_name: "新音色",
1824
+ voice_clone_preview_default_text: "我是 {name},你的私董会成员,很高兴为你服务。",
1825
+ voice_clone_preview_sample_placeholder: "试听文案",
1826
+ voice_clone_preview_btn_aria: "试听克隆音色",
1827
+ voice_clone_preview_hint: "可以编辑上面的文案,然后点播放按钮听一下克隆出的音色。",
1828
+ voice_clone_preview_err: "试听失败: {msg}",
1829
+ voice_clone_preview_audio_err: "浏览器无法解码音频,请试试不同的文案。",
1830
+ voice_clone_preview_missing_voice: "voice_id 没拿到,请重新打开克隆面板。",
1831
+ voice_clone_pill_label_short: "克隆中",
1832
+ voice_clone_pill_done: "已完成",
1833
+ voice_clone_pill_failed: "克隆失败",
1834
+ voice_clone_pill_aria: "展开音色克隆面板",
1835
+ voice_clone_err_provider_auth: "服务商拒绝了 API key,请到语音设置确认。",
1836
+ voice_clone_err_provider_quota: "服务商余额或订阅不支持克隆。",
1837
+ voice_clone_err_audio_too_short: "音频样本太短,无法用于克隆。",
1838
+ voice_clone_err_audio_too_long: "音频样本太长。",
1646
1839
  ap_voice_locked_providers: "已接入 <b>MiniMax</b> · <b>ElevenLabs</b>",
1647
1840
  ap_voice_modify_pitch: "音高",
1648
1841
  ap_voice_modify_intensity: "力度",
1649
1842
  ap_voice_modify_timbre: "音色",
1650
1843
  ap_voice_preview_btn_title: "试听",
1651
1844
  ap_voice_preview_sample: "你好,我是你的董事会成员,很高兴为你服务。",
1845
+ ap_voice_preview_text_label: "试听文案",
1846
+ ap_voice_section_voice: "音色",
1847
+ ap_voice_section_emotion: "情绪",
1848
+ ap_voice_section_preview: "试听文案",
1849
+ ap_voice_group_cloned_provider: "// 我克隆的音色 · {provider}",
1850
+ ap_voice_rename_btn: "重命名音色",
1851
+ ap_voice_rename_prompt: "为音色 {id} 起一个名字",
1852
+ ap_voice_rename_clear_confirm: "清除自定义名称,使用服务商提供的标签?",
1853
+ ap_voice_rename_err: "重命名失败: {msg}",
1652
1854
  ap_voice_err_missing_permissions_title: "API key 缺少 voices_read 权限",
1653
1855
  ap_voice_err_missing_permissions_body: "重新生成 API key 时勾选 voices_read scope,然后在 Voice Setup 重新粘贴。",
1654
1856
  ap_voice_err_auth_failed_title: "ElevenLabs 拒绝了这个 API key",
@@ -1711,6 +1913,35 @@ When the room ___, raise an objection.`,
1711
1913
 
1712
1914
  convene_show_more: "展开全文 ↓",
1713
1915
  convene_show_less: "收起 ↑",
1916
+ // Thread · 与某位 director 的私聊小窗
1917
+ thread_trigger: "// 私聊",
1918
+ thread_trigger_short: "私聊",
1919
+ thread_trigger_label: "私聊",
1920
+ thread_trigger_tip: "把 {name} 拉到一边单聊",
1921
+ thread_head_kicker: "你和他的私聊",
1922
+ thread_minimize: "—",
1923
+ thread_minimize_tip: "最小化",
1924
+ thread_close: "✕",
1925
+ thread_close_tip: "关闭",
1926
+ thread_empty_title: "拉到一边聊聊",
1927
+ thread_empty_deck: "想从 {name} 那里挖什么?他已经掌握当前房间的全部上下文。",
1928
+ thread_composer_placeholder: "向 {name} 提问…",
1929
+ thread_send: "发送",
1930
+ thread_stop: "停止",
1931
+ thread_stop_tip: "打断当前回复",
1932
+ thread_maximize: "⛶",
1933
+ thread_maximize_tip: "最大化到右侧面板",
1934
+ thread_restore: "⊟",
1935
+ thread_restore_tip: "还原为悬浮窗",
1936
+ thread_dock_resize_tip: "拖动调整面板宽度",
1937
+ head_threads_tip: "查看本房间所有私聊",
1938
+ head_threads_label: "所有私聊",
1939
+ threads_list_title: "// 私聊列表",
1940
+ threads_list_loading: "加载中…",
1941
+ threads_list_empty: "暂无私聊 · 在任意 director 消息上点击 // Thread 按钮开启一个。",
1942
+ threads_list_active: "进行中",
1943
+ threads_list_delete_tip: "删除这条私聊",
1944
+ threads_list_delete_confirm: "确认删除与 {name} 的私聊?删除后无法恢复。",
1714
1945
  convene_eyebrow: "▸ 召开 · 初始问题",
1715
1946
  convene_meta_to: "致",
1716
1947
 
@@ -1730,6 +1961,7 @@ When the room ___, raise an objection.`,
1730
1961
  msg_ws_query_label: "查询",
1731
1962
  msg_ws_expand_hide: "收起",
1732
1963
  msg_ws_secs: "{n} 秒",
1964
+ msg_ws_card_body: "已搜 \"{query}\"",
1733
1965
  msg_chair_direct_kicker: "▸ 主席 · 直接回复你",
1734
1966
  msg_chair_intervention_kicker: "▸ 主席说明",
1735
1967
  msg_chair_billing_kicker: "▸ 计费 · 需要处理",
@@ -1840,6 +2072,17 @@ When the room ___, raise an objection.`,
1840
2072
  rec_stop_cancel_mark: "↩ 取消",
1841
2073
  rec_stop_cancel_deck: "留在这里 · 录制继续。",
1842
2074
  rec_saved_toast: "录制已保存 · 文件已下载",
2075
+ rec_pause_save_class: "暂停 · 保存录制?",
2076
+ rec_pause_save_right: "// 进行中的录制",
2077
+ rec_pause_save_tag: "▸ 录制",
2078
+ rec_pause_save_title: "是否保存当前录制?",
2079
+ rec_pause_save_deck: "当前发言的董事已结束,房间已暂停。录制还在进行 — 请选择如何处理。",
2080
+ rec_pause_save_adjourn_mark: "✓ 保存并结束房间",
2081
+ rec_pause_save_adjourn_deck: "停止录制,下载文件,然后结束房间。",
2082
+ rec_pause_save_keep_mark: "✓ 保存并保持暂停",
2083
+ rec_pause_save_keep_deck: "停止录制并下载。房间保持暂停状态,稍后可以恢复。",
2084
+ rec_pause_save_continue_mark: "↩ 继续录制",
2085
+ rec_pause_save_continue_deck: "保持暂停 · 录制继续(注意:会录到一段静音)。",
1843
2086
  resume_class: "继续 · 选择",
1844
2087
  resume_right: "// 缺少语音密钥",
1845
2088
  resume_tag: "▸ 语音模式需要密钥",
@@ -2424,6 +2667,8 @@ When the room ___, raise an objection.`,
2424
2667
  us_active_llm_save_btn: "保存并启用",
2425
2668
  us_active_llm_save_failed: "保存失败 —— 请检查 key 后重试。",
2426
2669
  us_active_llm_delete_active_confirm: "正在删除当前使用的服务商 —— 删除后将自动切换到下一个已添加的服务商;如果没有其他服务商,系统将无法正常使用。确定继续吗?",
2670
+ us_llm_delete_confirm: "确认删除这条 LLM 凭证?删除后无法恢复。",
2671
+ us_provider_key_delete_confirm: "确认删除 {label} 的 API key?删除后无法恢复。",
2427
2672
  us_active_llm_switch_confirm: "切换当前服务商为 {label}?所有 director 会被重新分配到新服务商的模型。",
2428
2673
  voice_cred_section_title: "▸ 语音模型",
2429
2674
  voice_cred_section_deck: "支撑语音会议 · 所有 director 只从当前生效的语音模型里抽声音。",
@@ -2435,6 +2680,7 @@ When the room ___, raise an objection.`,
2435
2680
  voice_cred_picker_title: "选择一个语音模型",
2436
2681
  voice_cred_switch_confirm: "切换到 {label}?每位 director 的声音会从新模型的清单里重新分配。",
2437
2682
  voice_cred_delete_confirm_active: "删除正在使用的语音凭证?Director 的声音会从下一个可用凭证里重新分配。",
2683
+ voice_cred_delete_confirm: "确认删除这条语音凭证?删除后无法恢复。",
2438
2684
  voice_cred_save_failed: "保存语音凭证失败 — 请检查 key 后重试。",
2439
2685
  ap_voice_empty_no_active: "还没启用语音模型 · 去 Settings 添加一个",
2440
2686
  search_cred_section_title: "▸ 搜索服务",
@@ -2447,6 +2693,7 @@ When the room ___, raise an objection.`,
2447
2693
  search_cred_picker_title: "选择一个搜索服务",
2448
2694
  search_cred_switch_confirm: "切换到 {label}?之后 Web Search 都会走这把凭证。",
2449
2695
  search_cred_delete_confirm_active: "删除正在使用的搜索凭证?Web Search 会切换到下一个可用凭证;如果都没了就停止工作,直到你重新添加。",
2696
+ search_cred_delete_confirm: "确认删除这条搜索凭证?删除后无法恢复。",
2450
2697
  search_cred_save_failed: "保存搜索凭证失败 — 请检查 key 后重试。",
2451
2698
  us_keys_group_skill: "技能服务",
2452
2699
  us_keys_skill_deck: "为需要外部服务的系统技能授权。每位董事可在档案中单独开关。",
@@ -2884,6 +3131,20 @@ When the room ___, raise an objection.`,
2884
3131
 
2885
3132
  convene_show_more: "もっと見る ↓",
2886
3133
  convene_show_less: "閉じる ↑",
3134
+ // Thread · 1人のディレクターと1対1の個別会話
3135
+ thread_trigger: "// 個別",
3136
+ thread_trigger_short: "個別に返信",
3137
+ thread_trigger_label: "個別",
3138
+ thread_trigger_tip: "{name}を脇に呼んで個別に話す",
3139
+ thread_head_kicker: "あなたとの個別チャット",
3140
+ thread_minimize: "—",
3141
+ thread_minimize_tip: "最小化",
3142
+ thread_close: "✕",
3143
+ thread_close_tip: "閉じる",
3144
+ thread_empty_title: "脇に呼ぶ",
3145
+ thread_empty_deck: "{name}に何を聞きたい?この部屋のここまでの文脈を彼は全部把握しています。",
3146
+ thread_composer_placeholder: "{name}に質問…",
3147
+ thread_send: "送信",
2887
3148
  convene_eyebrow: "▸ 開始 · 最初の質問",
2888
3149
  convene_meta_to: "宛先",
2889
3150
 
@@ -3391,6 +3652,8 @@ When the room ___, raise an objection.`,
3391
3652
  us_active_llm_save_btn: "保存して有効化",
3392
3653
  us_active_llm_save_failed: "保存に失敗しました —— キーを確認して再試行してください。",
3393
3654
  us_active_llm_delete_active_confirm: "現在使用中のプロバイダーを削除します —— 削除後は別の追加済みプロバイダーに切り替わります。他になければシステムは動作しません。続行しますか?",
3655
+ us_llm_delete_confirm: "このLLMクレデンシャルを削除しますか?元には戻せません。",
3656
+ us_provider_key_delete_confirm: "{label} のAPIキーを削除しますか?元には戻せません。",
3394
3657
  us_active_llm_switch_confirm: "現在のプロバイダーを {label} に切り替えますか?全 director が新しいプロバイダーのモデルに再割り当てされます。",
3395
3658
  us_keys_skill_deck: "外部サービスを必要とするシステムスキルを有効化。各エージェントはプロフィール単位で参加可否を選べます。",
3396
3659
  us_ws_backend_tag: "ウェブ検索バックエンド",
@@ -3816,6 +4079,20 @@ When the room ___, raise an objection.`,
3816
4079
 
3817
4080
  convene_show_more: "Ver más ↓",
3818
4081
  convene_show_less: "Ver menos ↑",
4082
+ // Thread · conversación privada 1:1 con un director
4083
+ thread_trigger: "// privado",
4084
+ thread_trigger_short: "Responder en privado",
4085
+ thread_trigger_label: "Privado",
4086
+ thread_trigger_tip: "Aparta a {name} en privado",
4087
+ thread_head_kicker: "privado contigo",
4088
+ thread_minimize: "—",
4089
+ thread_minimize_tip: "Minimizar",
4090
+ thread_close: "✕",
4091
+ thread_close_tip: "Cerrar",
4092
+ thread_empty_title: "Apártalo",
4093
+ thread_empty_deck: "¿Qué quieres ahondar con {name}? Tiene todo el contexto de la sala hasta este punto.",
4094
+ thread_composer_placeholder: "Pregúntale a {name}…",
4095
+ thread_send: "Enviar",
3819
4096
  convene_eyebrow: "▸ Convocar · Pregunta inicial",
3820
4097
  convene_meta_to: "para",
3821
4098
 
@@ -4324,6 +4601,8 @@ When the room ___, raise an objection.`,
4324
4601
  us_active_llm_save_btn: "Guardar y activar",
4325
4602
  us_active_llm_save_failed: "No se pudo guardar la credencial — revisa la clave y vuelve a intentarlo.",
4326
4603
  us_active_llm_delete_active_confirm: "Eliminando el proveedor activo — la sala usará otro proveedor añadido, o dejará de funcionar hasta que añadas uno nuevo. ¿Continuar?",
4604
+ us_llm_delete_confirm: "¿Eliminar esta credencial LLM? No se puede deshacer.",
4605
+ us_provider_key_delete_confirm: "¿Eliminar la clave API de {label}? No se puede deshacer.",
4327
4606
  us_active_llm_switch_confirm: "¿Cambiar el proveedor LLM activo a {label}? Todos los directores se reasignarán a los modelos del nuevo proveedor.",
4328
4607
  us_keys_skill_deck: "habilita habilidades de sistema que necesitan un servicio externo. Cada agente puede optar in/out en su perfil.",
4329
4608
  us_ws_backend_tag: "MOTOR DE BÚSQUEDA WEB",
Binary file