@jini-ai/ui 0.6.15 → 0.6.17
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.
- package/CHANGELOG.md +19 -0
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/features/appearance/constants.d.ts +28 -1
- package/dist/features/appearance/constants.js +12 -9
- package/dist/features/appearance/react/components/AppearanceTab.js +4 -4
- package/dist/features/charts/contract.d.ts +4 -4
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/wire.d.ts +19 -0
- package/dist/features/charts/wire.js +20 -2
- package/dist/features/execution/constants.js +0 -1
- package/dist/features/folder-path-drop/rules.d.ts +2 -3
- package/dist/features/folder-path-drop/rules.js +3 -4
- package/dist/features/i18n/dictionaries/settings-dialog.ar.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.bn.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.de.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.en.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.es.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fa.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fr.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hi.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hu.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.id.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.it.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ko.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pl.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ru.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.th.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.tr.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.uk.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ur.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +11 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +11 -0
- package/dist/features/mcp-ui/surfaces/checkbox.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/choice-group.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/choice-group.js +2 -2
- package/dist/features/mcp-ui/surfaces/document.d.ts +11 -1
- package/dist/features/mcp-ui/surfaces/document.js +9 -1
- package/dist/features/mcp-ui/surfaces/fields.d.ts +7 -1
- package/dist/features/mcp-ui/surfaces/fields.js +12 -11
- package/dist/features/mcp-ui/surfaces/form.js +6 -1
- package/dist/features/mcp-ui/surfaces/select.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/text-input.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/tool-approval.js +4 -3
- package/dist/react/components/AgentIcon.js +0 -1
- package/dist/react/components/agent-icon-data-uris.generated.js +0 -1
- package/dist/renderers/shiki.js +7 -1
- package/package.json +32 -30
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_JA = {
|
|
2
|
+
"Folders ({count})": "フォルダー({count})",
|
|
3
|
+
"Copy folder path: {path}": "フォルダーのパスをコピー: {path}",
|
|
4
|
+
"Remove folder: {path}": "フォルダーを削除: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "メッセージからキーと思われる文字列を削除しました。本物のキーだった場合は再発行してください。保存には安全なフォームを使ってください。",
|
|
3
6
|
// eslint-disable-next-line sonarjs/no-hardcoded-passwords -- Translation of user-facing credential-safety guidance; the value is prose, not a password.
|
|
4
7
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "キー、トークン、パスワード、シークレットの保存には安全なフォームを使ってください。チャットの回答はアシスタントに送信されます。",
|
|
@@ -69,6 +72,14 @@ export const SETTINGS_DIALOG_JA = {
|
|
|
69
72
|
'Custom instructions': 'カスタム指示',
|
|
70
73
|
'Custom model id': 'カスタムモデルID',
|
|
71
74
|
'Custom…': 'カスタム…',
|
|
75
|
+
'Charcoal': 'チャコール',
|
|
76
|
+
'Clay': 'クレイ',
|
|
77
|
+
'Violet': 'バイオレット',
|
|
78
|
+
'Green': 'グリーン',
|
|
79
|
+
'Red': 'レッド',
|
|
80
|
+
'Amber': 'アンバー',
|
|
81
|
+
'Teal': 'ティール',
|
|
82
|
+
'Pink': 'ピンク',
|
|
72
83
|
'Default accent color': '既定のアクセントカラー',
|
|
73
84
|
'Default endpoint. Usually no need to change this.': '既定のエンドポイントです。通常は変更する必要はありません。',
|
|
74
85
|
'Default model': '既定のモデル',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_KO = {
|
|
2
|
+
"Folders ({count})": "폴더 ({count})",
|
|
3
|
+
"Copy folder path: {path}": "폴더 경로 복사: {path}",
|
|
4
|
+
"Remove folder: {path}": "폴더 제거: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "메시지에서 키로 보이는 내용을 제거했습니다. 실제 키였다면 교체하세요. 저장하려면 보안 양식을 사용하세요.",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "키, 토큰, 비밀번호, 비밀 정보를 저장하려면 보안 양식을 사용하세요. 채팅 답변은 어시스턴트에게 전송됩니다.",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# 내 스킬\n\n1. 워크플로를 설명하세요.\n2. 입력과 출력을 설명하세요.',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_KO = {
|
|
|
68
71
|
'Custom instructions': '사용자 지정 지침',
|
|
69
72
|
'Custom model id': '사용자 지정 모델 ID',
|
|
70
73
|
'Custom…': '사용자 지정…',
|
|
74
|
+
'Charcoal': '차콜',
|
|
75
|
+
'Clay': '클레이',
|
|
76
|
+
'Violet': '바이올렛',
|
|
77
|
+
'Green': '초록',
|
|
78
|
+
'Red': '빨강',
|
|
79
|
+
'Amber': '호박색',
|
|
80
|
+
'Teal': '청록',
|
|
81
|
+
'Pink': '분홍',
|
|
71
82
|
'Default accent color': '기본 강조 색상',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': '기본 엔드포인트입니다. 보통 변경할 필요가 없습니다.',
|
|
73
84
|
'Default model': '기본 모델',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_PL = {
|
|
2
|
+
"Folders ({count})": "Foldery ({count})",
|
|
3
|
+
"Copy folder path: {path}": "Kopiuj ścieżkę folderu: {path}",
|
|
4
|
+
"Remove folder: {path}": "Usuń folder: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "Usunąłem z wiadomości tekst, który wyglądał jak klucz. Jeśli był prawdziwy, wymień go. Zapisz go za pomocą bezpiecznego formularza.",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "Użyj bezpiecznego formularza do zapisywania kluczy, tokenów, haseł i sekretów. Odpowiedzi na czacie są wysyłane do asystenta.",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# Moja umiejętność\n\n1. Opisz przebieg pracy.\n2. Opisz dane wejściowe i wyjściowe.',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_PL = {
|
|
|
68
71
|
'Custom instructions': 'Niestandardowe instrukcje',
|
|
69
72
|
'Custom model id': 'Niestandardowy identyfikator modelu',
|
|
70
73
|
'Custom…': 'Niestandardowy…',
|
|
74
|
+
'Charcoal': 'Grafitowy',
|
|
75
|
+
'Clay': 'Gliniany',
|
|
76
|
+
'Violet': 'Fioletowy',
|
|
77
|
+
'Green': 'Zielony',
|
|
78
|
+
'Red': 'Czerwony',
|
|
79
|
+
'Amber': 'Bursztynowy',
|
|
80
|
+
'Teal': 'Morski',
|
|
81
|
+
'Pink': 'Różowy',
|
|
71
82
|
'Default accent color': 'Domyślny kolor akcentu',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'Domyślny punkt końcowy. Zwykle nie trzeba go zmieniać.',
|
|
73
84
|
'Default model': 'Model domyślny',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_PT_BR = {
|
|
2
|
+
"Folders ({count})": "Pastas ({count})",
|
|
3
|
+
"Copy folder path: {path}": "Copiar caminho da pasta: {path}",
|
|
4
|
+
"Remove folder: {path}": "Remover pasta: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "Removi da sua mensagem o que parecia ser uma chave. Se era real, substitua-a. Use o formulário seguro para salvá-la.",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "Use o formulário seguro para salvar chaves, tokens, senhas e segredos. As respostas do chat são enviadas ao assistente.",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# Minha skill\n\n1. Explique o fluxo de trabalho.\n2. Descreva as entradas e saídas.',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_PT_BR = {
|
|
|
68
71
|
'Custom instructions': 'Instruções personalizadas',
|
|
69
72
|
'Custom model id': 'ID de modelo personalizado',
|
|
70
73
|
'Custom…': 'Personalizado…',
|
|
74
|
+
'Charcoal': 'Carvão',
|
|
75
|
+
'Clay': 'Argila',
|
|
76
|
+
'Violet': 'Violeta',
|
|
77
|
+
'Green': 'Verde',
|
|
78
|
+
'Red': 'Vermelho',
|
|
79
|
+
'Amber': 'Âmbar',
|
|
80
|
+
'Teal': 'Azul-petróleo',
|
|
81
|
+
'Pink': 'Rosa',
|
|
71
82
|
'Default accent color': 'Cor de destaque padrão',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'Endpoint padrão. Geralmente não é necessário alterar isso.',
|
|
73
84
|
'Default model': 'Modelo padrão',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_RU = {
|
|
2
|
+
"Folders ({count})": "Папки ({count})",
|
|
3
|
+
"Copy folder path: {path}": "Копировать путь папки: {path}",
|
|
4
|
+
"Remove folder: {path}": "Убрать папку: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "Я удалил из сообщения то, что было похоже на ключ. Если он настоящий, замените его. Сохраните его через защищённую форму.",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "Используйте защищённую форму для сохранения ключей, токенов, паролей и секретов. Ответы в чате отправляются ассистенту.",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# Мой навык\n\n1. Опишите рабочий процесс.\n2. Опишите входные и выходные данные.',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_RU = {
|
|
|
68
71
|
'Custom instructions': 'Пользовательские инструкции',
|
|
69
72
|
'Custom model id': 'Пользовательский id. модели',
|
|
70
73
|
'Custom…': 'Другое…',
|
|
74
|
+
'Charcoal': 'Угольный',
|
|
75
|
+
'Clay': 'Глиняный',
|
|
76
|
+
'Violet': 'Фиолетовый',
|
|
77
|
+
'Green': 'Зелёный',
|
|
78
|
+
'Red': 'Красный',
|
|
79
|
+
'Amber': 'Янтарный',
|
|
80
|
+
'Teal': 'Бирюзовый',
|
|
81
|
+
'Pink': 'Розовый',
|
|
71
82
|
'Default accent color': 'Акцентный цвет по умолчанию',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'Конечная точка по умолчанию. Обычно менять её не требуется.',
|
|
73
84
|
'Default model': 'Модель по умолчанию',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_TH = {
|
|
2
|
+
"Folders ({count})": "โฟลเดอร์ ({count})",
|
|
3
|
+
"Copy folder path: {path}": "คัดลอกพาธโฟลเดอร์: {path}",
|
|
4
|
+
"Remove folder: {path}": "นำโฟลเดอร์ออก: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "ฉันลบข้อความที่ดูเหมือนคีย์ออกจากข้อความของคุณแล้ว หากเป็นคีย์จริง ให้เปลี่ยนคีย์ใหม่ ใช้แบบฟอร์มที่ปลอดภัยเพื่อบันทึก",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "ใช้แบบฟอร์มที่ปลอดภัยเพื่อบันทึกคีย์ โทเค็น รหัสผ่าน และข้อมูลลับ คำตอบในแชตจะถูกส่งไปยังผู้ช่วย",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# ทักษะของฉัน\n\n1. อธิบายขั้นตอนการทำงาน\n2. อธิบายข้อมูลนำเข้าและผลลัพธ์',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_TH = {
|
|
|
68
71
|
'Custom instructions': 'คำสั่งที่กำหนดเอง',
|
|
69
72
|
'Custom model id': 'รหัสโมเดลที่กำหนดเอง',
|
|
70
73
|
'Custom…': 'กำหนดเอง…',
|
|
74
|
+
'Charcoal': 'ถ่าน',
|
|
75
|
+
'Clay': 'ดินเผา',
|
|
76
|
+
'Violet': 'ม่วง',
|
|
77
|
+
'Green': 'เขียว',
|
|
78
|
+
'Red': 'แดง',
|
|
79
|
+
'Amber': 'อำพัน',
|
|
80
|
+
'Teal': 'เขียวน้ำทะเล',
|
|
81
|
+
'Pink': 'ชมพู',
|
|
71
82
|
'Default accent color': 'สีเน้นเริ่มต้น',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'เอนด์พอยต์เริ่มต้น โดยปกติไม่จำเป็นต้องเปลี่ยนค่านี้',
|
|
73
84
|
'Default model': 'โมเดลเริ่มต้น',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_TR = {
|
|
2
|
+
"Folders ({count})": "Klasörler ({count})",
|
|
3
|
+
"Copy folder path: {path}": "Klasör yolunu kopyala: {path}",
|
|
4
|
+
"Remove folder: {path}": "Klasörü kaldır: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "Mesajınızdan anahtara benzeyen metni kaldırdım. Gerçekse yenileyin. Kaydetmek için güvenli formu kullanın.",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "Anahtarları, tokenları, parolaları ve gizli bilgileri kaydetmek için güvenli formu kullanın. Sohbet yanıtları asistana gönderilir.",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# Becerim\n\n1. İş akışını açıkla.\n2. Girdileri ve çıktıları tanımla.',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_TR = {
|
|
|
68
71
|
'Custom instructions': 'Özel talimatlar',
|
|
69
72
|
'Custom model id': 'Özel model kimliği',
|
|
70
73
|
'Custom…': 'Özel…',
|
|
74
|
+
'Charcoal': 'Kömür',
|
|
75
|
+
'Clay': 'Kil',
|
|
76
|
+
'Violet': 'Mor',
|
|
77
|
+
'Green': 'Yeşil',
|
|
78
|
+
'Red': 'Kırmızı',
|
|
79
|
+
'Amber': 'Kehribar',
|
|
80
|
+
'Teal': 'Camgöbeği',
|
|
81
|
+
'Pink': 'Pembe',
|
|
71
82
|
'Default accent color': 'Varsayılan vurgu rengi',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'Varsayılan uç nokta. Genellikle bunu değiştirmeye gerek yoktur.',
|
|
73
84
|
'Default model': 'Varsayılan model',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_UK = {
|
|
2
|
+
"Folders ({count})": "Теки ({count})",
|
|
3
|
+
"Copy folder path: {path}": "Копіювати шлях теки: {path}",
|
|
4
|
+
"Remove folder: {path}": "Прибрати теку: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "Я видалив із повідомлення те, що було схоже на ключ. Якщо він справжній, замініть його. Збережіть його через захищену форму.",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "Використовуйте захищену форму для збереження ключів, токенів, паролів і секретів. Відповіді в чаті надсилаються асистенту.",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# Моя навичка\n\n1. Опишіть робочий процес.\n2. Опишіть вхідні та вихідні дані.',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_UK = {
|
|
|
68
71
|
'Custom instructions': 'Власні інструкції',
|
|
69
72
|
'Custom model id': 'Власний ідентифікатор моделі',
|
|
70
73
|
'Custom…': 'Власний…',
|
|
74
|
+
'Charcoal': 'Вугільний',
|
|
75
|
+
'Clay': 'Глиняний',
|
|
76
|
+
'Violet': 'Фіолетовий',
|
|
77
|
+
'Green': 'Зелений',
|
|
78
|
+
'Red': 'Червоний',
|
|
79
|
+
'Amber': 'Бурштиновий',
|
|
80
|
+
'Teal': 'Бірюзовий',
|
|
81
|
+
'Pink': 'Рожевий',
|
|
71
82
|
'Default accent color': 'Акцентний колір за замовчуванням',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'Кінцева точка за замовчуванням. Зазвичай змінювати її не потрібно.',
|
|
73
84
|
'Default model': 'Модель за замовчуванням',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_UR = {
|
|
2
|
+
"Folders ({count})": "فولڈرز ({count})",
|
|
3
|
+
"Copy folder path: {path}": "فولڈر کا راستہ کاپی کریں: {path}",
|
|
4
|
+
"Remove folder: {path}": "فولڈر ہٹائیں: {path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "میں نے آپ کے پیغام سے وہ متن ہٹا دیا جو کلید جیسا لگ رہا تھا۔ اگر وہ حقیقی تھی تو اسے تبدیل کریں۔ محفوظ کرنے کے لیے محفوظ فارم استعمال کریں۔",
|
|
3
6
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "کلیدیں، ٹوکن، پاس ورڈ اور راز محفوظ کرنے کے لیے محفوظ فارم استعمال کریں۔ چیٹ کے جوابات اسسٹنٹ کو بھیجے جاتے ہیں۔",
|
|
4
7
|
'# My skill\n\n1. Explain the workflow.\n2. Describe the inputs and outputs.': '# میری اسکل\n\n1. ورک فلو کی وضاحت کریں۔\n2. ان پٹس اور آؤٹ پٹس بیان کریں۔',
|
|
@@ -68,6 +71,14 @@ export const SETTINGS_DIALOG_UR = {
|
|
|
68
71
|
'Custom instructions': 'کسٹم ہدایات',
|
|
69
72
|
'Custom model id': 'کسٹم ماڈل آئی ڈی',
|
|
70
73
|
'Custom…': 'کسٹم…',
|
|
74
|
+
'Charcoal': 'کوئلہ',
|
|
75
|
+
'Clay': 'مٹی',
|
|
76
|
+
'Violet': 'بنفشی',
|
|
77
|
+
'Green': 'سبز',
|
|
78
|
+
'Red': 'سرخ',
|
|
79
|
+
'Amber': 'کہربائی',
|
|
80
|
+
'Teal': 'نیلگوں سبز',
|
|
81
|
+
'Pink': 'گلابی',
|
|
71
82
|
'Default accent color': 'ڈیفالٹ نمایاں رنگ',
|
|
72
83
|
'Default endpoint. Usually no need to change this.': 'ڈیفالٹ اینڈ پوائنٹ۔ عام طور پر اسے تبدیل کرنے کی ضرورت نہیں ہوتی۔',
|
|
73
84
|
'Default model': 'ڈیفالٹ ماڈل',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_ZH_CN = {
|
|
2
|
+
"Folders ({count})": "文件夹({count})",
|
|
3
|
+
"Copy folder path: {path}": "复制文件夹路径:{path}",
|
|
4
|
+
"Remove folder: {path}": "移除文件夹:{path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "我已从你的消息中移除疑似密钥的内容。如果是真实密钥,请更换它。请使用安全表单保存。",
|
|
3
6
|
// eslint-disable-next-line sonarjs/no-hardcoded-passwords -- Translation of user-facing credential-safety guidance; the value is prose, not a password.
|
|
4
7
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "请使用安全表单保存密钥、令牌、密码和机密信息。聊天回答会发送给助手。",
|
|
@@ -69,6 +72,14 @@ export const SETTINGS_DIALOG_ZH_CN = {
|
|
|
69
72
|
'Custom instructions': '自定义指令',
|
|
70
73
|
'Custom model id': '自定义模型 ID',
|
|
71
74
|
'Custom…': '自定义…',
|
|
75
|
+
'Charcoal': '炭灰',
|
|
76
|
+
'Clay': '陶土',
|
|
77
|
+
'Violet': '紫罗兰',
|
|
78
|
+
'Green': '绿色',
|
|
79
|
+
'Red': '红色',
|
|
80
|
+
'Amber': '琥珀色',
|
|
81
|
+
'Teal': '青色',
|
|
82
|
+
'Pink': '粉色',
|
|
72
83
|
'Default accent color': '默认强调色',
|
|
73
84
|
'Default endpoint. Usually no need to change this.': '默认终结点,通常无需更改。',
|
|
74
85
|
'Default model': '默认模型',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export const SETTINGS_DIALOG_ZH_TW = {
|
|
2
|
+
"Folders ({count})": "資料夾({count})",
|
|
3
|
+
"Copy folder path: {path}": "複製資料夾路徑:{path}",
|
|
4
|
+
"Remove folder: {path}": "移除資料夾:{path}",
|
|
2
5
|
"I removed what looked like a key from your message. If it was real, rotate it. Use the secure form to save it.": "我已從你的訊息中移除疑似金鑰的內容。如果是真實金鑰,請更換它。請使用安全表單儲存。",
|
|
3
6
|
// eslint-disable-next-line sonarjs/no-hardcoded-passwords -- Translation of user-facing credential-safety guidance; the value is prose, not a password.
|
|
4
7
|
"Use the secure form to save keys, tokens, passwords, and secrets. Chat answers are sent to the assistant.": "請使用安全表單儲存金鑰、權杖、密碼和機密資訊。聊天回答會傳送給助理。",
|
|
@@ -69,6 +72,14 @@ export const SETTINGS_DIALOG_ZH_TW = {
|
|
|
69
72
|
'Custom instructions': '自訂指令',
|
|
70
73
|
'Custom model id': '自訂模型 ID',
|
|
71
74
|
'Custom…': '自訂…',
|
|
75
|
+
'Charcoal': '炭灰',
|
|
76
|
+
'Clay': '陶土',
|
|
77
|
+
'Violet': '紫羅蘭',
|
|
78
|
+
'Green': '綠色',
|
|
79
|
+
'Red': '紅色',
|
|
80
|
+
'Amber': '琥珀色',
|
|
81
|
+
'Teal': '青色',
|
|
82
|
+
'Pink': '粉紅色',
|
|
72
83
|
'Default accent color': '預設強調色',
|
|
73
84
|
'Default endpoint. Usually no need to change this.': '預設端點,通常不需要變更。',
|
|
74
85
|
'Default model': '預設模型',
|
|
@@ -13,6 +13,8 @@ export interface CheckboxProps {
|
|
|
13
13
|
*/
|
|
14
14
|
readonly required?: boolean;
|
|
15
15
|
readonly disabled?: boolean;
|
|
16
|
+
/** Visually hide the label (it repeats the card title); it stays the accessible name. */
|
|
17
|
+
readonly labelHidden?: boolean;
|
|
16
18
|
/**
|
|
17
19
|
* One extra `data-*` attribute on the `<input>`, escaped like every other value. For a caller
|
|
18
20
|
* that needs to look the box up by something other than its DOM `name` — e.g. the confirmation
|
|
@@ -22,6 +22,8 @@ export interface ChoiceGroupProps {
|
|
|
22
22
|
*/
|
|
23
23
|
readonly required?: boolean;
|
|
24
24
|
readonly disabled?: boolean;
|
|
25
|
+
/** Visually hide the legend (it repeats the card title); it stays the group's accessible name. */
|
|
26
|
+
readonly labelHidden?: boolean;
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Renders one grouped-choice field.
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* value unless normalised — see `form.ts`'s `readChecked`, which is written against this fact.
|
|
31
31
|
*/
|
|
32
32
|
import { escapeHtml } from '../escape.js';
|
|
33
|
-
import { fieldElementId } from './document.js';
|
|
33
|
+
import { fieldElementId, VISUALLY_HIDDEN_CLASS } from './document.js';
|
|
34
34
|
/**
|
|
35
35
|
* Renders one grouped-choice field.
|
|
36
36
|
*
|
|
@@ -63,7 +63,7 @@ export function renderChoiceGroup(props) {
|
|
|
63
63
|
})
|
|
64
64
|
.join('\n');
|
|
65
65
|
return (`<fieldset class="mcpui-field mcpui-choice-group"${describedBy}>\n` +
|
|
66
|
-
`<legend class="mcpui-label">${escapeHtml(props.label)}${required}</legend>${hint}\n` +
|
|
66
|
+
`<legend class="mcpui-label${props.labelHidden === true ? ` ${VISUALLY_HIDDEN_CLASS}` : ''}">${escapeHtml(props.label)}${required}</legend>${hint}\n` +
|
|
67
67
|
`${options}\n` +
|
|
68
68
|
`</fieldset>`);
|
|
69
69
|
}
|
|
@@ -10,7 +10,7 @@ export type { SurfaceDetail } from './structured-details.js';
|
|
|
10
10
|
* be a color no caller could reskin. `var(--x, fallback)` appears only where a value is genuinely
|
|
11
11
|
* optional.
|
|
12
12
|
*/
|
|
13
|
-
export declare const SURFACE_BASE_CSS = "*, *::before, *::after { box-sizing: border-box; }\nhtml, body { margin: 0; padding: 0; }\nbody {\n /* Transparent, not var(--jini-mcpui-bg): this document has no page of its own to paint. It is an\n allow-scripts-only sandboxed iframe embedded directly in a host's chat transcript, and html's\n background is left at its (also transparent) initial value, so the host's own pane shows\n straight through to .mcpui-surface below rather than through a second, unrelated rectangle of\n fill color sitting between the two. --jini-mcpui-bg stays declared for a caller styling\n something else via extraCss -- nothing in this stylesheet reads it anymore. */\n background: transparent;\n color: var(--jini-mcpui-text);\n font-family: var(--jini-mcpui-font);\n font-size: 14px;\n line-height: 1.5;\n padding: 16px;\n -webkit-font-smoothing: antialiased;\n}\n.mcpui-surface {\n background: linear-gradient(135deg,\n color-mix(in srgb, var(--jini-mcpui-accent) 5%, var(--jini-mcpui-panel)) 0%,\n var(--jini-mcpui-panel) 60%);\n border: 1px solid color-mix(in srgb, var(--jini-mcpui-accent) 18%, var(--jini-mcpui-border));\n border-radius: var(--jini-mcpui-radius-md);\n box-shadow: var(--jini-mcpui-surface-shadow);\n padding: 18px 20px 16px;\n max-width: 640px;\n margin: 0 auto;\n}\n.mcpui-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--jini-mcpui-text-strong);\n}\n.mcpui-description { margin: 6px 0 0; color: var(--jini-mcpui-text-muted); }\n/* fit-content(40%), not max-content: one long label used to take the whole row and squeeze every\n value to one character per line. Labels wrap at 40% instead. */\n.mcpui-details {\n display: grid;\n grid-template-columns: fit-content(40%) minmax(0, 1fr);\n gap: 4px 14px;\n margin: 14px 0 0;\n}\n.mcpui-details dt { color: var(--jini-mcpui-text-soft); font-weight: 500; overflow-wrap: anywhere; }\n.mcpui-details dd { margin: 0; overflow-wrap: anywhere; color: var(--jini-mcpui-text); }\n.mcpui-details-outcome { display: flex; flex-direction: column; gap: 8px; }\n.mcpui-outcome-row { display: flex; align-items: flex-start; gap: 14px; }\n.mcpui-outcome-row dt { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.mcpui-outcome-row dt .mcpui-hint { display: block; font-weight: 400; }\n.mcpui-outcome-row dd { flex: 1; min-width: 0; }\n/* Capped so a long status cannot squeeze the label back to one character per line; the full text\n stays in the cell's title. */\n.mcpui-outcome-row dd.mcpui-detail-status {\n flex: 0 1 auto; max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n/* Frames up to 480px: stack each label above its value so the value gets the full width.\n Outcome lists keep their own row layout. */\n@media (max-width: 480px) {\n .mcpui-details:not(.mcpui-details-outcome) { grid-template-columns: minmax(0, 1fr); row-gap: 0; }\n .mcpui-details:not(.mcpui-details-outcome) dd { margin-bottom: 8px; }\n}\n/* The iframe has 16px body padding on each side: 352px viewport = 320px card. Stacked, the value\n has the full width, so it wraps between words instead of truncating. */\n@media (max-width: 352px) {\n .mcpui-outcome-row { flex-direction: column; gap: 2px; }\n .mcpui-outcome-row dd.mcpui-detail-status { max-width: 100%; white-space: normal; overflow-wrap: normal; }\n}\n.mcpui-code {\n margin: 0;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n max-height: 16em;\n overflow: auto;\n font-family: var(--jini-mcpui-font-mono);\n font-size: 12.5px;\n line-height: 1.45;\n padding: 6px 8px;\n border: 1px solid var(--jini-mcpui-border);\n border-radius: var(--jini-mcpui-radius-sm);\n background: var(--jini-mcpui-bg);\n}\n.mcpui-warning {\n margin: 14px 0 0;\n padding: 9px 11px;\n border-radius: var(--jini-mcpui-radius-md);\n border: 1px solid var(--jini-mcpui-danger);\n background: var(--jini-mcpui-danger-tint);\n color: var(--jini-mcpui-text-strong);\n}\n.mcpui-fields { display: flex; flex-direction: column; gap: 12px; margin: 16px 0 0; border: 0; padding: 0; }\n.mcpui-field { display: flex; flex-direction: column; gap: 4px; }\n.mcpui-field-inline { flex-direction: row; align-items: flex-start; gap: 8px; }\n.mcpui-label { font-weight: 500; color: var(--jini-mcpui-text-strong); }\n.mcpui-required { color: var(--jini-mcpui-accent); margin-left: 2px; }\n.mcpui-hint { color: var(--jini-mcpui-text-soft); font-size: 13px; }\n.mcpui-input, .mcpui-select, .mcpui-textarea {\n font: inherit;\n color: var(--jini-mcpui-text);\n background: var(--jini-mcpui-panel);\n border: 1px solid color-mix(in srgb, var(--jini-mcpui-accent) 14%, var(--jini-mcpui-border-strong));\n border-radius: var(--jini-mcpui-radius-sm);\n padding: 7px 9px;\n width: 100%;\n min-width: 0;\n}\n.mcpui-textarea { resize: vertical; min-height: 72px; font-family: var(--jini-mcpui-font); }\n/* A secret control's eye toggle sits inside the field's right edge. The control is padded so long\n values never run under it; the button is a 40px tap target centred on the input, transparent,\n so it overhangs the ~36px input invisibly instead of resizing the field. */\n.mcpui-secret { position: relative; display: flex; flex-direction: column; }\n.mcpui-secret > .mcpui-input, .mcpui-secret > .mcpui-textarea { padding-right: 44px; }\n.mcpui-secret-toggle {\n position: absolute;\n top: 50%;\n right: 0;\n width: 40px;\n height: 40px;\n margin-top: -20px;\n padding: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n border-radius: var(--jini-mcpui-radius-sm);\n background: transparent;\n color: var(--jini-mcpui-text-soft);\n cursor: pointer;\n}\n.mcpui-secret > .mcpui-textarea + .mcpui-secret-toggle { top: 0; margin-top: -2px; }\n.mcpui-secret-toggle:hover:not([disabled]) { color: var(--jini-mcpui-text-strong); }\n.mcpui-secret-toggle[disabled] { opacity: 0.55; cursor: default; }\n.mcpui-secret-toggle:focus-visible { outline: 2px solid var(--jini-mcpui-accent-ring); outline-offset: -6px; }\n.mcpui-secret-toggle[aria-pressed=\"true\"] .mcpui-secret-icon-show,\n.mcpui-secret-toggle:not([aria-pressed=\"true\"]) .mcpui-secret-icon-hide { display: none; }\n.mcpui-checkbox { margin: 3px 0 0; accent-color: var(--jini-mcpui-accent); }\n.mcpui-choice-group { border: 0; padding: 0; margin: 0; min-width: 0; }\n.mcpui-choice { display: flex; flex-direction: row; align-items: flex-start; gap: 8px; margin: 6px 0 0; }\n.mcpui-choice-label { color: var(--jini-mcpui-text); font-weight: 400; }\n.mcpui-input:focus-visible, .mcpui-select:focus-visible, .mcpui-textarea:focus-visible,\n.mcpui-checkbox:focus-visible, .mcpui-button:focus-visible {\n outline: 2px solid var(--jini-mcpui-accent-ring);\n outline-offset: 1px;\n border-color: var(--jini-mcpui-accent);\n}\n.mcpui-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 0; }\n.mcpui-button {\n font: inherit;\n font-weight: 500;\n padding: 7px 15px;\n border-radius: var(--jini-mcpui-radius-pill);\n border: 1px solid color-mix(in srgb, var(--jini-mcpui-accent) 14%, var(--jini-mcpui-border-strong));\n background: var(--jini-mcpui-panel);\n color: var(--jini-mcpui-text);\n cursor: pointer;\n}\n.mcpui-button:hover:not([disabled]) { background: var(--jini-mcpui-accent-tint); }\n.mcpui-button-primary {\n background: var(--jini-mcpui-accent);\n border-color: var(--jini-mcpui-accent);\n color: var(--jini-mcpui-accent-ink);\n}\n.mcpui-button-primary:hover:not([disabled]) { background: var(--jini-mcpui-accent-hover); border-color: var(--jini-mcpui-accent-hover); }\n.mcpui-button-danger {\n background: var(--jini-mcpui-danger);\n border-color: var(--jini-mcpui-danger);\n color: var(--jini-mcpui-panel);\n}\n.mcpui-button-danger:hover:not([disabled]) { background: var(--jini-mcpui-danger-hover); border-color: var(--jini-mcpui-danger-hover); }\n.mcpui-button[disabled] { opacity: 0.55; cursor: default; }\n.mcpui-status { margin: 12px 0 0; min-height: 1.4em; color: var(--jini-mcpui-text-muted); }\n.mcpui-status[data-state=\"failed\"], .mcpui-status[data-state=\"invalid\"], .mcpui-status[data-state=\"expired\"] { color: var(--jini-mcpui-danger); }\n.mcpui-status[data-state=\"done\"] { color: var(--jini-mcpui-text-strong); }\n/* Neither a plain success nor a failure \u2014 e.g. an outcome surface reporting \"uploaded, not yet\n reachable\" (outcome.ts's own state: partial). Distinct from both --jini-mcpui-danger (would\n wrongly read as \"this failed\") and --jini-mcpui-text-strong (would wrongly read as \"this is\n fully done\"); the accent token reads as \"worth a second look\" without asserting either. */\n.mcpui-status[data-state=\"partial\"] { color: var(--jini-mcpui-accent); }\n.mcpui-code { font-family: var(--jini-mcpui-font-mono); font-size: 13px; color: var(--jini-mcpui-text-faint); }";
|
|
13
|
+
export declare const SURFACE_BASE_CSS = "*, *::before, *::after { box-sizing: border-box; }\nhtml, body { margin: 0; padding: 0; }\nbody {\n /* Transparent, not var(--jini-mcpui-bg): this document has no page of its own to paint. It is an\n allow-scripts-only sandboxed iframe embedded directly in a host's chat transcript, and html's\n background is left at its (also transparent) initial value, so the host's own pane shows\n straight through to .mcpui-surface below rather than through a second, unrelated rectangle of\n fill color sitting between the two. --jini-mcpui-bg stays declared for a caller styling\n something else via extraCss -- nothing in this stylesheet reads it anymore. */\n background: transparent;\n color: var(--jini-mcpui-text);\n font-family: var(--jini-mcpui-font);\n font-size: 14px;\n line-height: 1.5;\n padding: 16px;\n -webkit-font-smoothing: antialiased;\n}\n.mcpui-surface {\n background: linear-gradient(135deg,\n color-mix(in srgb, var(--jini-mcpui-accent) 5%, var(--jini-mcpui-panel)) 0%,\n var(--jini-mcpui-panel) 60%);\n border: 1px solid color-mix(in srgb, var(--jini-mcpui-accent) 18%, var(--jini-mcpui-border));\n border-radius: var(--jini-mcpui-radius-md);\n box-shadow: var(--jini-mcpui-surface-shadow);\n padding: 18px 20px 16px;\n max-width: 640px;\n margin: 0 auto;\n}\n.mcpui-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--jini-mcpui-text-strong);\n}\n.mcpui-description { margin: 6px 0 0; color: var(--jini-mcpui-text-muted); }\n/* fit-content(40%), not max-content: one long label used to take the whole row and squeeze every\n value to one character per line. Labels wrap at 40% instead. */\n.mcpui-details {\n display: grid;\n grid-template-columns: fit-content(40%) minmax(0, 1fr);\n gap: 4px 14px;\n margin: 14px 0 0;\n}\n.mcpui-details dt { color: var(--jini-mcpui-text-soft); font-weight: 500; overflow-wrap: anywhere; }\n.mcpui-details dd { margin: 0; overflow-wrap: anywhere; color: var(--jini-mcpui-text); }\n.mcpui-details dt .mcpui-hint { display: block; font-weight: 400; }\n/* A nested group (a tool's input fields) gets the card's full width, not the narrow value column. */\n.mcpui-details > dt:has(+ dd > .mcpui-detail-group),\n.mcpui-details > dd:has(> .mcpui-detail-group) { grid-column: 1 / -1; }\n.mcpui-details > dt:has(+ dd > .mcpui-detail-group) { margin-bottom: 6px; }\n.mcpui-details-outcome { display: flex; flex-direction: column; gap: 8px; }\n.mcpui-outcome-row { display: flex; align-items: flex-start; gap: 14px; }\n.mcpui-outcome-row dt { flex: 1; min-width: 0; overflow-wrap: anywhere; }\n.mcpui-outcome-row dt .mcpui-hint { display: block; font-weight: 400; }\n.mcpui-outcome-row dd { flex: 1; min-width: 0; }\n/* Capped so a long status cannot squeeze the label back to one character per line; the full text\n stays in the cell's title. */\n.mcpui-outcome-row dd.mcpui-detail-status {\n flex: 0 1 auto; max-width: 60%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n/* Frames up to 480px: stack each label above its value so the value gets the full width.\n Outcome lists keep their own row layout. */\n@media (max-width: 480px) {\n .mcpui-details:not(.mcpui-details-outcome) { grid-template-columns: minmax(0, 1fr); row-gap: 0; }\n .mcpui-details:not(.mcpui-details-outcome) dd { margin-bottom: 8px; }\n}\n/* The iframe has 16px body padding on each side: 352px viewport = 320px card. Stacked, the value\n has the full width, so it wraps between words instead of truncating. */\n@media (max-width: 352px) {\n .mcpui-outcome-row { flex-direction: column; gap: 2px; }\n .mcpui-outcome-row dd.mcpui-detail-status { max-width: 100%; white-space: normal; overflow-wrap: normal; }\n}\n.mcpui-code {\n margin: 0;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n max-height: 16em;\n overflow: auto;\n font-family: var(--jini-mcpui-font-mono);\n font-size: 12.5px;\n line-height: 1.45;\n padding: 6px 8px;\n border: 1px solid var(--jini-mcpui-border);\n border-radius: var(--jini-mcpui-radius-sm);\n background: var(--jini-mcpui-bg);\n}\n.mcpui-warning {\n margin: 14px 0 0;\n padding: 9px 11px;\n border-radius: var(--jini-mcpui-radius-md);\n border: 1px solid var(--jini-mcpui-danger);\n background: var(--jini-mcpui-danger-tint);\n color: var(--jini-mcpui-text-strong);\n}\n.mcpui-fields { display: flex; flex-direction: column; gap: 12px; margin: 16px 0 0; border: 0; padding: 0; }\n.mcpui-field { display: flex; flex-direction: column; gap: 4px; }\n.mcpui-field-inline { flex-direction: row; align-items: flex-start; gap: 8px; }\n.mcpui-label { font-weight: 500; color: var(--jini-mcpui-text-strong); }\n.mcpui-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }\n.mcpui-required { color: var(--jini-mcpui-accent); margin-left: 2px; }\n.mcpui-hint { color: var(--jini-mcpui-text-soft); font-size: 13px; }\n.mcpui-input, .mcpui-select, .mcpui-textarea {\n font: inherit;\n color: var(--jini-mcpui-text);\n background: var(--jini-mcpui-panel);\n border: 1px solid color-mix(in srgb, var(--jini-mcpui-accent) 14%, var(--jini-mcpui-border-strong));\n border-radius: var(--jini-mcpui-radius-sm);\n padding: 7px 9px;\n width: 100%;\n min-width: 0;\n}\n.mcpui-textarea { resize: vertical; min-height: 72px; font-family: var(--jini-mcpui-font); }\n/* A secret control's eye toggle sits inside the field's right edge. The control is padded so long\n values never run under it; the button is a 40px tap target centred on the input, transparent,\n so it overhangs the ~36px input invisibly instead of resizing the field. */\n.mcpui-secret { position: relative; display: flex; flex-direction: column; }\n.mcpui-secret > .mcpui-input, .mcpui-secret > .mcpui-textarea { padding-right: 44px; }\n.mcpui-secret-toggle {\n position: absolute;\n top: 50%;\n right: 0;\n width: 40px;\n height: 40px;\n margin-top: -20px;\n padding: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 0;\n border-radius: var(--jini-mcpui-radius-sm);\n background: transparent;\n color: var(--jini-mcpui-text-soft);\n cursor: pointer;\n}\n.mcpui-secret > .mcpui-textarea + .mcpui-secret-toggle { top: 0; margin-top: -2px; }\n.mcpui-secret-toggle:hover:not([disabled]) { color: var(--jini-mcpui-text-strong); }\n.mcpui-secret-toggle[disabled] { opacity: 0.55; cursor: default; }\n.mcpui-secret-toggle:focus-visible { outline: 2px solid var(--jini-mcpui-accent-ring); outline-offset: -6px; }\n.mcpui-secret-toggle[aria-pressed=\"true\"] .mcpui-secret-icon-show,\n.mcpui-secret-toggle:not([aria-pressed=\"true\"]) .mcpui-secret-icon-hide { display: none; }\n.mcpui-checkbox { margin: 3px 0 0; accent-color: var(--jini-mcpui-accent); }\n.mcpui-choice-group { border: 0; padding: 0; margin: 0; min-width: 0; }\n.mcpui-choice { display: flex; flex-direction: row; align-items: flex-start; gap: 8px; margin: 6px 0 0; }\n.mcpui-choice-label { color: var(--jini-mcpui-text); font-weight: 400; }\n.mcpui-input:focus-visible, .mcpui-select:focus-visible, .mcpui-textarea:focus-visible,\n.mcpui-checkbox:focus-visible, .mcpui-button:focus-visible {\n outline: 2px solid var(--jini-mcpui-accent-ring);\n outline-offset: 1px;\n border-color: var(--jini-mcpui-accent);\n}\n.mcpui-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 0; }\n.mcpui-button {\n font: inherit;\n font-weight: 500;\n padding: 7px 15px;\n border-radius: var(--jini-mcpui-radius-pill);\n border: 1px solid color-mix(in srgb, var(--jini-mcpui-accent) 14%, var(--jini-mcpui-border-strong));\n background: var(--jini-mcpui-panel);\n color: var(--jini-mcpui-text);\n cursor: pointer;\n}\n.mcpui-button:hover:not([disabled]) { background: var(--jini-mcpui-accent-tint); }\n.mcpui-button-primary {\n background: var(--jini-mcpui-accent);\n border-color: var(--jini-mcpui-accent);\n color: var(--jini-mcpui-accent-ink);\n}\n.mcpui-button-primary:hover:not([disabled]) { background: var(--jini-mcpui-accent-hover); border-color: var(--jini-mcpui-accent-hover); }\n.mcpui-button-danger {\n background: var(--jini-mcpui-danger);\n border-color: var(--jini-mcpui-danger);\n color: var(--jini-mcpui-panel);\n}\n.mcpui-button-danger:hover:not([disabled]) { background: var(--jini-mcpui-danger-hover); border-color: var(--jini-mcpui-danger-hover); }\n.mcpui-button[disabled] { opacity: 0.55; cursor: default; }\n.mcpui-status { margin: 12px 0 0; min-height: 1.4em; color: var(--jini-mcpui-text-muted); }\n.mcpui-status[data-state=\"failed\"], .mcpui-status[data-state=\"invalid\"], .mcpui-status[data-state=\"expired\"] { color: var(--jini-mcpui-danger); }\n.mcpui-status[data-state=\"done\"] { color: var(--jini-mcpui-text-strong); }\n/* Neither a plain success nor a failure \u2014 e.g. an outcome surface reporting \"uploaded, not yet\n reachable\" (outcome.ts's own state: partial). Distinct from both --jini-mcpui-danger (would\n wrongly read as \"this failed\") and --jini-mcpui-text-strong (would wrongly read as \"this is\n fully done\"); the accent token reads as \"worth a second look\" without asserting either. */\n.mcpui-status[data-state=\"partial\"] { color: var(--jini-mcpui-accent); }\n.mcpui-code { font-family: var(--jini-mcpui-font-mono); font-size: 13px; color: var(--jini-mcpui-text-faint); }";
|
|
14
14
|
/**
|
|
15
15
|
* The Content-Security-Policy every generated surface declares in its own `<head>`.
|
|
16
16
|
*
|
|
@@ -118,6 +118,14 @@ export declare const DEFAULT_SURFACE_STATUS_TEXT: SurfaceStatusText;
|
|
|
118
118
|
* the builders make no assumption about it beyond ES5.
|
|
119
119
|
*/
|
|
120
120
|
export declare const SURFACE_SCRIPT_PRELUDE: string;
|
|
121
|
+
/**
|
|
122
|
+
* A field name that is simultaneously an HTML `name`, part of a DOM `id`, and a key in the JSON
|
|
123
|
+
* params object the surface posts back. Anything outside this set breaks at least one of those
|
|
124
|
+
* three silently — a space in a `name` produces an `id` the `for` attribute cannot reference, so
|
|
125
|
+
* clicking the label stops focusing the control and nothing reports an error.
|
|
126
|
+
*/
|
|
127
|
+
/** Hides an element visually while keeping it in the accessibility tree (see `.mcpui-visually-hidden` in {@link SURFACE_BASE_CSS}). */
|
|
128
|
+
export declare const VISUALLY_HIDDEN_CLASS = "mcpui-visually-hidden";
|
|
121
129
|
/**
|
|
122
130
|
* Builds the DOM id for a field's control, validating the name on the way through.
|
|
123
131
|
*
|
|
@@ -136,6 +144,8 @@ export declare function renderFieldLabel(spec: {
|
|
|
136
144
|
label: string;
|
|
137
145
|
required?: boolean;
|
|
138
146
|
hint?: string;
|
|
147
|
+
/** Visually hide the label (it repeats the card title); it stays the control's accessible name. */
|
|
148
|
+
labelHidden?: boolean;
|
|
139
149
|
}): string;
|
|
140
150
|
/** The `aria-describedby` attribute pointing at a field's hint, or `''` when it has none. */
|
|
141
151
|
export declare function fieldDescribedBy(spec: {
|
|
@@ -70,6 +70,11 @@ body {
|
|
|
70
70
|
}
|
|
71
71
|
.mcpui-details dt { color: var(--jini-mcpui-text-soft); font-weight: 500; overflow-wrap: anywhere; }
|
|
72
72
|
.mcpui-details dd { margin: 0; overflow-wrap: anywhere; color: var(--jini-mcpui-text); }
|
|
73
|
+
.mcpui-details dt .mcpui-hint { display: block; font-weight: 400; }
|
|
74
|
+
/* A nested group (a tool's input fields) gets the card's full width, not the narrow value column. */
|
|
75
|
+
.mcpui-details > dt:has(+ dd > .mcpui-detail-group),
|
|
76
|
+
.mcpui-details > dd:has(> .mcpui-detail-group) { grid-column: 1 / -1; }
|
|
77
|
+
.mcpui-details > dt:has(+ dd > .mcpui-detail-group) { margin-bottom: 6px; }
|
|
73
78
|
.mcpui-details-outcome { display: flex; flex-direction: column; gap: 8px; }
|
|
74
79
|
.mcpui-outcome-row { display: flex; align-items: flex-start; gap: 14px; }
|
|
75
80
|
.mcpui-outcome-row dt { flex: 1; min-width: 0; overflow-wrap: anywhere; }
|
|
@@ -118,6 +123,7 @@ body {
|
|
|
118
123
|
.mcpui-field { display: flex; flex-direction: column; gap: 4px; }
|
|
119
124
|
.mcpui-field-inline { flex-direction: row; align-items: flex-start; gap: 8px; }
|
|
120
125
|
.mcpui-label { font-weight: 500; color: var(--jini-mcpui-text-strong); }
|
|
126
|
+
.mcpui-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
|
|
121
127
|
.mcpui-required { color: var(--jini-mcpui-accent); margin-left: 2px; }
|
|
122
128
|
.mcpui-hint { color: var(--jini-mcpui-text-soft); font-size: 13px; }
|
|
123
129
|
.mcpui-input, .mcpui-select, .mcpui-textarea {
|
|
@@ -382,6 +388,8 @@ function reportCallFailure(error) {
|
|
|
382
388
|
* three silently — a space in a `name` produces an `id` the `for` attribute cannot reference, so
|
|
383
389
|
* clicking the label stops focusing the control and nothing reports an error.
|
|
384
390
|
*/
|
|
391
|
+
/** Hides an element visually while keeping it in the accessibility tree (see `.mcpui-visually-hidden` in {@link SURFACE_BASE_CSS}). */
|
|
392
|
+
export const VISUALLY_HIDDEN_CLASS = 'mcpui-visually-hidden';
|
|
385
393
|
const FIELD_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
|
|
386
394
|
/**
|
|
387
395
|
* Builds the DOM id for a field's control, validating the name on the way through.
|
|
@@ -406,7 +414,7 @@ export function renderFieldLabel(spec) {
|
|
|
406
414
|
const hint = spec.hint === undefined
|
|
407
415
|
? ''
|
|
408
416
|
: `\n<span class="mcpui-hint" id="${escapeHtml(fieldElementId(spec.name))}-hint">${escapeHtml(spec.hint)}</span>`;
|
|
409
|
-
return `<label class="mcpui-label" for="${escapeHtml(fieldElementId(spec.name))}">${escapeHtml(spec.label)}${required}</label>${hint}`;
|
|
417
|
+
return `<label class="mcpui-label${spec.labelHidden === true ? ` ${VISUALLY_HIDDEN_CLASS}` : ''}" for="${escapeHtml(fieldElementId(spec.name))}">${escapeHtml(spec.label)}${required}</label>${hint}`;
|
|
410
418
|
}
|
|
411
419
|
/** The `aria-describedby` attribute pointing at a field's hint, or `''` when it has none. */
|
|
412
420
|
export function fieldDescribedBy(spec) {
|
|
@@ -72,15 +72,21 @@ export interface MultiEnumField extends SurfaceFieldBase {
|
|
|
72
72
|
readonly options: readonly SelectOption[];
|
|
73
73
|
}
|
|
74
74
|
export type SurfaceField = StringField | NumberField | BooleanField | EnumField | MultiEnumField;
|
|
75
|
+
/** Presentation the form decides for a field, not part of the field's own data. */
|
|
76
|
+
export interface FieldRenderOptions {
|
|
77
|
+
/** The label repeats the card title: keep it as the accessible name, but don't show it twice. */
|
|
78
|
+
readonly labelHidden?: boolean;
|
|
79
|
+
}
|
|
75
80
|
/**
|
|
76
81
|
* Renders the control fragment for one field, dispatching on its kind.
|
|
77
82
|
*
|
|
78
83
|
* @param field - See {@link SurfaceField}.
|
|
79
84
|
* @param text - Optional host translations for secret reveal controls.
|
|
85
|
+
* @param options - See {@link FieldRenderOptions}.
|
|
80
86
|
* @returns An HTML fragment.
|
|
81
87
|
* @complexity O(n) in the rendered length.
|
|
82
88
|
*/
|
|
83
|
-
export declare function renderFieldControl(field: SurfaceField, text?: Partial<SecretInputText
|
|
89
|
+
export declare function renderFieldControl(field: SurfaceField, text?: Partial<SecretInputText>, options?: FieldRenderOptions): string;
|
|
84
90
|
/** What the surface's inline script needs to read a field's value back and coerce it — the runtime half of {@link SurfaceField}, with the presentation stripped out. */
|
|
85
91
|
export interface FieldReadSpec {
|
|
86
92
|
readonly name: string;
|
|
@@ -31,41 +31,42 @@ function present(source, keys) {
|
|
|
31
31
|
}
|
|
32
32
|
return picked;
|
|
33
33
|
}
|
|
34
|
-
function base(field) {
|
|
35
|
-
return { name: field.name, label: field.label, ...present(field, ['hint', 'required', 'disabled']) };
|
|
34
|
+
function base(field, options = {}) {
|
|
35
|
+
return { name: field.name, label: field.label, ...present(field, ['hint', 'required', 'disabled']), ...(options.labelHidden === true ? { labelHidden: true } : {}) };
|
|
36
36
|
}
|
|
37
37
|
/** One-of-N, drawn as radios or a select per {@link EnumField.presentation}. @complexity O(n) in the rendered length. */
|
|
38
|
-
function renderEnumField(field) {
|
|
38
|
+
function renderEnumField(field, options) {
|
|
39
39
|
if (field.presentation === 'radio') {
|
|
40
40
|
return renderChoiceGroup({
|
|
41
|
-
...base(field),
|
|
41
|
+
...base(field, options),
|
|
42
42
|
selection: 'single',
|
|
43
43
|
options: field.options,
|
|
44
44
|
...(field.value === undefined ? {} : { value: [field.value] }),
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
-
return renderSelect({ ...base(field), options: field.options, ...present(field, ['value', 'placeholder']) });
|
|
47
|
+
return renderSelect({ ...base(field, options), options: field.options, ...present(field, ['value', 'placeholder']) });
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Renders the control fragment for one field, dispatching on its kind.
|
|
51
51
|
*
|
|
52
52
|
* @param field - See {@link SurfaceField}.
|
|
53
53
|
* @param text - Optional host translations for secret reveal controls.
|
|
54
|
+
* @param options - See {@link FieldRenderOptions}.
|
|
54
55
|
* @returns An HTML fragment.
|
|
55
56
|
* @complexity O(n) in the rendered length.
|
|
56
57
|
*/
|
|
57
|
-
export function renderFieldControl(field, text) {
|
|
58
|
+
export function renderFieldControl(field, text, options = {}) {
|
|
58
59
|
switch (field.kind) {
|
|
59
60
|
case 'string':
|
|
60
|
-
return renderTextInput({ ...base(field), ...text, ...present(field, ['value', 'placeholder', 'multiline', 'rows', 'secret']) });
|
|
61
|
+
return renderTextInput({ ...base(field, options), ...text, ...present(field, ['value', 'placeholder', 'multiline', 'rows', 'secret']) });
|
|
61
62
|
case 'number':
|
|
62
|
-
return renderTextInput({ ...base(field), inputType: 'number', ...present(field, ['value', 'placeholder', 'min', 'max', 'step']) });
|
|
63
|
+
return renderTextInput({ ...base(field, options), inputType: 'number', ...present(field, ['value', 'placeholder', 'min', 'max', 'step']) });
|
|
63
64
|
case 'boolean':
|
|
64
|
-
return renderCheckbox({ ...base(field), ...present(field, ['value']) });
|
|
65
|
+
return renderCheckbox({ ...base(field, options), ...present(field, ['value']) });
|
|
65
66
|
case 'enum':
|
|
66
|
-
return renderEnumField(field);
|
|
67
|
+
return renderEnumField(field, options);
|
|
67
68
|
case 'multi-enum':
|
|
68
|
-
return renderChoiceGroup({ ...base(field), selection: 'multiple', options: field.options, ...present(field, ['value']) });
|
|
69
|
+
return renderChoiceGroup({ ...base(field, options), selection: 'multiple', options: field.options, ...present(field, ['value']) });
|
|
69
70
|
}
|
|
70
71
|
return unhandledCase(field);
|
|
71
72
|
}
|
|
@@ -54,10 +54,15 @@ function formActions(spec) {
|
|
|
54
54
|
actions.push({ id: 'cancel', label: spec.cancel.label, variant: 'neutral' });
|
|
55
55
|
return actions;
|
|
56
56
|
}
|
|
57
|
+
/** Case- and whitespace-insensitive equality: a field label that only repeats the card title is the same question. */
|
|
58
|
+
function sameText(a, b) {
|
|
59
|
+
const norm = (s) => s.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
60
|
+
return norm(a) === norm(b);
|
|
61
|
+
}
|
|
57
62
|
/** Header, context, the `<form>` itself and the status region. @complexity O(n) in the rendered length. */
|
|
58
63
|
function formBodyHtml(spec, text) {
|
|
59
64
|
const controls = spec.fields
|
|
60
|
-
.map((field) => renderFieldControl(field, { showSecret: text.showSecret, hideSecret: text.hideSecret }))
|
|
65
|
+
.map((field) => renderFieldControl(field, { showSecret: text.showSecret, hideSecret: text.hideSecret }, { labelHidden: sameText(field.label, spec.title) }))
|
|
61
66
|
.join('\n');
|
|
62
67
|
const warning = spec.warning === undefined ? '' : `<p class="mcpui-warning">${escapeHtml(spec.warning)}</p>`;
|
|
63
68
|
return [
|
|
@@ -23,6 +23,8 @@ export interface SelectProps {
|
|
|
23
23
|
/** Label for that leading empty option. Defaults to an em-dash. */
|
|
24
24
|
readonly placeholder?: string;
|
|
25
25
|
readonly disabled?: boolean;
|
|
26
|
+
/** Visually hide the label (it repeats the card title); it stays the accessible name. */
|
|
27
|
+
readonly labelHidden?: boolean;
|
|
26
28
|
}
|
|
27
29
|
/**
|
|
28
30
|
* Renders one enumerated field.
|
|
@@ -16,6 +16,8 @@ export interface TextInputProps extends Partial<SecretInputText> {
|
|
|
16
16
|
readonly hint?: string;
|
|
17
17
|
readonly required?: boolean;
|
|
18
18
|
readonly disabled?: boolean;
|
|
19
|
+
/** Visually hide the label (it repeats the card title); it stays the accessible name. */
|
|
20
|
+
readonly labelHidden?: boolean;
|
|
19
21
|
/** `'number'` also constrains the on-screen keyboard on touch devices, which `'text'` would not. */
|
|
20
22
|
readonly inputType?: 'text' | 'number';
|
|
21
23
|
/** Renders as `<input type="password">`, or a masked textarea when multiline — either way with
|
|
@@ -7,15 +7,16 @@ function record({ value }, _options = {}) {
|
|
|
7
7
|
const candidate = { value };
|
|
8
8
|
return isNonArrayRecord(candidate) ? candidate.value : {};
|
|
9
9
|
}
|
|
10
|
-
/** Project inline JSON-schema property
|
|
11
|
-
*
|
|
10
|
+
/** Project inline JSON-schema property titles into the existing dotted-path label contract.
|
|
11
|
+
* Only `title`: `description` is guidance written for the model (often a paragraph), not a label;
|
|
12
|
+
* untitled fields keep the renderer's humanized-key fallback. No reference resolution or validation.
|
|
12
13
|
* @complexity O(n * depth) time and space in property-path bytes; no effects or input mutation. */
|
|
13
14
|
function schemaLabels({ schema, path = '' }, { text }) {
|
|
14
15
|
const properties = record({ value: record({ value: schema }).properties });
|
|
15
16
|
return Object.fromEntries(Object.entries(properties).flatMap(([key, property]) => {
|
|
16
17
|
const field = record({ value: property });
|
|
17
18
|
const fieldPath = path ? `${path}.${key}` : key;
|
|
18
|
-
const label = nonBlank({ value: field.title })
|
|
19
|
+
const label = nonBlank({ value: field.title });
|
|
19
20
|
return [
|
|
20
21
|
...(label ? [[fieldPath, text({ key: label })]] : []),
|
|
21
22
|
...Object.entries(schemaLabels({ schema: field, path: fieldPath }, { text })),
|
|
@@ -7,7 +7,6 @@ export const AGENT_ICON_DATA_URIS = {
|
|
|
7
7
|
"antigravity": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0yIC0xIDI4IDI4Ij48cGF0aCBkPSJNMjEuNzUxIDIyLjYwN2MxLjM0IDEuMDA1IDMuMzUuMzM1IDEuNTA4LTEuNTA4QzE3LjczIDE1Ljc0IDE4LjkwNCAxIDEyLjAzNyAxIDUuMTcgMSA2LjM0MiAxNS43NC44MTUgMjEuMWMtMi4wMSAyLjAwOS4xNjcgMi41MTEgMS41MDcgMS41MDYgNS4xOTItMy41MTcgNC44NTctOS43MTQgOS43MTUtOS43MTQgNC44NTcgMCA0LjUyMiA2LjE5NyA5LjcxNCA5LjcxNXoiIGZpbGw9InVybCgjYWcpIi8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhZyIgeDE9IjIiIHkxPSIxMiIgeDI9IjIyIiB5Mj0iMTIiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBzdG9wLWNvbG9yPSIjNDI4NUY0Ii8+PHN0b3Agb2Zmc2V0PSIuMzMiIHN0b3AtY29sb3I9IiNFQTQzMzUiLz48c3RvcCBvZmZzZXQ9Ii42NiIgc3RvcC1jb2xvcj0iI0ZCQkMwNCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzM0QTg1MyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjwvc3ZnPgo=",
|
|
8
8
|
"claude": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI2Q5Nzc1NyIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMjAuOTk4IDEwLjk0OUgyNHYzLjEwMmgtM3YzLjAyOGgtMS40ODdWMjBIMTh2LTIuOTIxaC0xLjQ4N1YyMEgxNXYtMi45MjFIOVYyMEg3LjQ4OHYtMi45MjFINlYyMEg0LjQ4N3YtMi45MjFIM1YxNC4wNUgwdi0zLjFoM1Y1aDE3Ljk5OHpNNiAxMC45NDloMS40ODhWOC4xMDJINnptMTAuNTEgMEgxOFY4LjEwMmgtMS40OXoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==",
|
|
9
9
|
"codex": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE5LjUwMyAwSDQuNDk2QTQuNDk2IDQuNDk2IDAgMCAwIDAgNC40OTZ2MTUuMDA3QTQuNDk2IDQuNDk2IDAgMCAwIDQuNDk2IDI0aDE1LjAwN0E0LjQ5NiA0LjQ5NiAwIDAgMCAyNCAxOS41MDNWNC40OTZBNC40OTYgNC40OTYgMCAwIDAgMTkuNTAzIDAiLz48cGF0aCBmaWxsPSJ1cmwoI2EpIiBkPSJNOS4wNjQgMy4zNDRhNC42IDQuNiAwIDAgMSAyLjI4NS0uMzEycTEuNS4xNzMgMi42NzMgMS4yNzUuMDE2LjAxNS4wMzcuMDIxYS4xLjEgMCAwIDAgLjA0MyAwIDQuNTUgNC41NSAwIDAgMSAzLjA0Ni4yNzVsLjA0Ny4wMjIuMTE2LjA1N2E0LjU4IDQuNTggMCAwIDEgMi4xODggMi4zOTlxLjMxMy43NjUuMzE1IDEuNTk1YTQuMiA0LjIgMCAwIDEtLjEzNCAxLjIyMy4xMi4xMiAwIDAgMCAuMDMuMTE1cS44OS45MSAxLjE4MyAyLjE3LjQzMyAyLjEzOC0uODg3IDMuODU0bC0uMTM2LjE2NmE0LjU1IDQuNTUgMCAwIDEtMi4yMDEgMS4zODguMTIuMTIgMCAwIDAtLjA4MS4wNzZjLS4xOTEuNTUxLS4zODMgMS4wMjMtLjc0IDEuNDk0LS45IDEuMTg3LTIuMjIyIDEuODQ2LTMuNzExIDEuODM4cS0xLjc4LS4wMDktMy4xNTctMS4zMDJhLjExLjExIDAgMCAwLS4xMDUtLjAyNGMtLjM4OC4xMjUtLjc4LjE0My0xLjIwNC4xMzhhNC40NCA0LjQ0IDAgMCAxLTEuOTQ1LS40NjYgNC41NCA0LjU0IDAgMCAxLTEuNjEtMS4zMzVjLS4xNTItLjIwMi0uMzAzLS4zOTItLjQxNC0uNjE3YTYgNiAwIDAgMS0uMzctLjk2MSA0LjYgNC42IDAgMCAxLS4wMTQtMi4yOTguMS4xIDAgMCAwIC4wMDYtLjA1Ni4xLjEgMCAwIDAtLjAyNy0uMDQ4IDQuNSA0LjUgMCAwIDEtMS4wMzQtMS42NTEgMy45IDMuOSAwIDAgMS0uMjUxLTEuMTkyIDUuMiA1LjIgMCAwIDEgLjE0MS0xLjZRMy42NTkgNy45MiA1LjA4NiA2Ljk3cS4zMTgtLjIxMi42MDEtLjMzYTYgNiAwIDAgMSAuNjQ2LS4yMjcuMS4xIDAgMCAwIC4wNjUtLjA2NiA0LjUgNC41IDAgMCAxIC44MjktMS42MTUgNC41NCA0LjU0IDAgMCAxIDEuODM3LTEuMzg4bTMuNDgyIDEwLjU2NWEuNjM3LjYzNyAwIDAgMCAwIDEuMjcyaDMuNjM2YS42MzcuNjM3IDAgMSAwIDAtMS4yNzJ6TTguNDYyIDkuMjNhLjYzNy42MzcgMCAwIDAtMS4xMDYuNjMxbDEuMjcyIDIuMjI0LTEuMjY2IDIuMTM2YS42MzYuNjM2IDAgMSAwIDEuMDk1LjY0OWwxLjQ1NC0yLjQ1NWEuNjQuNjQgMCAwIDAgLjAwNS0uNjR6Ii8+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMTIiIHgyPSIxMiIgeTE9IjMiIHkyPSIyMSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIHN0b3AtY29sb3I9IiNiMWE3ZmYiLz48c3RvcCBvZmZzZXQ9Ii41IiBzdG9wLWNvbG9yPSIjN2E5ZGZmIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMzk0MWZmIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PC9zdmc+",
|
|
10
|
-
"copilot": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iIzg1MzRGMyIgZD0iTTIzLjkyMiAxNi45OTJjLS44NjEgMS40OTUtNS44NTkgNS4wMjMtMTEuOTIyIDUuMDIzLTYuMDYzIDAtMTEuMDYxLTMuNTI4LTExLjkyMi01LjAyM0EuNjQxLjY0MSAwIDAgMSAwIDE2LjczNnYtMi44NjlhLjg0MS44NDEgMCAwIDEgLjA1My0uMjJjLjM3Mi0uOTM1IDEuMzQ3LTIuMjkyIDIuNjA1LTIuNjU2LjE2Ny0uNDI5LjQxNC0xLjA1NS42NDQtMS41MTdhMTAuMTk1IDEwLjE5NSAwIDAgMS0uMDUyLTEuMDg2YzAtMS4zMzEuMjgyLTIuNDk5IDEuMTMyLTMuMzY4LjM5Ny0uNDA2Ljg5LS43MTcgMS40NzQtLjk1MiAxLjM5OS0xLjEzNiAzLjM5Mi0yLjA5MyA2LjEyMi0yLjA5MyAyLjczMSAwIDQuNzY3Ljk1NyA2LjE2NiAyLjA5My41ODQuMjM1IDEuMDc3LjU0NiAxLjQ3NC45NTIuODUuODY5IDEuMTMyIDIuMDM3IDEuMTMyIDMuMzY4IDAgLjM2OC0uMDE0LjczMy0uMDUyIDEuMDg2LjIzLjQ2Mi40NzcgMS4wODguNjQ0IDEuNTE3IDEuMjU4LjM2NCAyLjIzMyAxLjcyMSAyLjYwNSAyLjY1NmEuODMyLjgzMiAwIDAgMSAuMDUzLjIydjIuODY5YS42NDEuNjQxIDAgMCAxLS4wNzguMjU2Wk0xMi4xNzIgMTFoLS4zNDRhNC4zMjMgNC4zMjMgMCAwIDEtLjM1NS41MDhDMTAuNzAzIDEyLjQ1NSA5LjU1NSAxMyA3Ljk2NSAxM2MtMS43MjUgMC0yLjk4OS0uMzU5LTMuNzgyLTEuMjU5YTIuMDA1IDIuMDA1IDAgMCAxLS4wODUtLjEwNEw0IDExLjc0MXY2LjU4NWMxLjQzNS43NzkgNC41MTQgMi4xNzkgOCAyLjE3OSAzLjQ4NiAwIDYuNTY1LTEuNCA4LTIuMTc5di02LjU4NWwtLjA5OC0uMTA0cy0uMDMzLjA0NS0uMDg1LjEwNGMtLjc5My45LTIuMDU3IDEuMjU5LTMuNzgyIDEuMjU5LTEuNTkgMC0yLjczOC0uNTQ1LTMuNTA4LTEuNDkyYTQuMzIzIDQuMzIzIDAgMCAxLS4zNTUtLjUwOGgtLjAxNi4wMTZabS42NDEtMi45MzVjLjEzNiAxLjA1Ny40MDMgMS45MTMuODc4IDIuNDk3LjQ0Mi41NDQgMS4xMzQuOTM4IDIuMzQ0LjkzOCAxLjU3MyAwIDIuMjkyLS4zMzcgMi42NTctLjc1MS4zODQtLjQzNS41NTgtMS4xNS41NTgtMi4zNjEgMC0xLjE0LS4yNDMtMS44NDctLjcwNS0yLjMxOS0uNDc3LS40ODgtMS4zMTktLjg2Mi0yLjgyNC0xLjAyNS0xLjQ4Ny0uMTYxLTIuMTkyLjEzOC0yLjUzMy41MjktLjI2OS4zMDctLjQzNy44MDgtLjQzOCAxLjU3OHYuMDIxYzAgLjI2NS4wMjEuNTYyLjA2My44OTNabS0xLjYyNiAwYy4wNDItLjMzMS4wNjMtLjYyOC4wNjMtLjg5NHYtLjAyYy0uMDAxLS43Ny0uMTY5LTEuMjcxLS40MzgtMS41NzgtLjM0MS0uMzkxLTEuMDQ2LS42OS0yLjUzMy0uNTI5LTEuNTA1LjE2My0yLjM0Ny41MzctMi44MjQgMS4wMjUtLjQ2Mi40NzItLjcwNSAxLjE3OS0uNzA1IDIuMzE5IDAgMS4yMTEuMTc1IDEuOTI2LjU1OCAyLjM2MS4zNjUuNDE0IDEuMDg0Ljc1MSAyLjY1Ny43NTEgMS4yMSAwIDEuOTAyLS4zOTQgMi4zNDQtLjkzOC40NzUtLjU4NC43NDItMS40NC44NzgtMi40OTdaIi8+PHBhdGggZmlsbD0iIzg1MzRGMyIgZD0iTTE0LjUgMTQuMjVhMSAxIDAgMCAxIDEgMXYyYTEgMSAwIDAgMS0yIDB2LTJhMSAxIDAgMCAxIDEtMVptLTUgMGExIDEgMCAwIDEgMSAxdjJhMSAxIDAgMCAxLTIgMHYtMmExIDEgMCAwIDEgMS0xWiIvPjwvc3ZnPg==",
|
|
11
10
|
"cursor-agent": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiMxYzFiMWEiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMjIuMTA2IDUuNjggMTIuNS4xMzVhMSAxIDAgMCAwLS45OTggMEwxLjg5MyA1LjY4YS44NC44NCAwIDAgMC0uNDE5LjcyNnYxMS4xODZjMCAuMy4xNi41NzcuNDIuNzI3bDkuNjA3IDUuNTQ3YTEgMSAwIDAgMCAuOTk4IDBsOS42MDgtNS41NDdhLjg0Ljg0IDAgMCAwIC40Mi0uNzI3VjYuNDA3YS44NC44NCAwIDAgMC0uNDItLjcyNnptLS42MDMgMS4xNzZMMTIuMjI4IDIyLjkyYy0uMDYzLjEwOC0uMjI4LjA2NC0uMjI4LS4wNjFWMTIuMzRhLjU5LjU5IDAgMCAwLS4yOTUtLjUxbC05LjExLTUuMjZjLS4xMDctLjA2Mi0uMDYzLS4yMjguMDYyLS4yMjhoMTguNTVjLjI2NCAwIC40MjguMjg2LjI5Ni41MTQiLz48L3N2Zz4=",
|
|
12
11
|
"deepseek": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iIzRkNmJmZSIgZD0iTTIzLjc0OCA0LjQ4MmMtLjI1NC0uMTI0LS4zNjQuMTEzLS41MTIuMjM0LS4wNTEuMDM5LS4wOTQuMDktLjEzNy4xMzYtLjM3Mi4zOTctLjgwNi42NTctMS4zNzMuNjI2LS44MjktLjA0Ni0xLjUzNy4yMTQtMi4xNjMuODQ4LS4xMzMtLjc4Mi0uNTc1LTEuMjQ4LTEuMjQ3LTEuNTQ4LS4zNTItLjE1Ni0uNzA4LS4zMTEtLjk1NS0uNjUtLjE3Mi0uMjQxLS4yMTktLjUxLS4zMDUtLjc3NC0uMDU1LS4xNi0uMTEtLjMyMy0uMjkzLS4zNS0uMi0uMDMxLS4yNzguMTM2LS4zNTYuMjc2LS4zMTMuNTcyLS40MzQgMS4yMDItLjQyMiAxLjg0LjAyNyAxLjQzNi42MzMgMi41OCAxLjgzOCAzLjM5My4xMzcuMDkzLjE3Mi4xODcuMTI5LjMyMy0uMDgyLjI4LS4xOC41NTItLjI2Ni44MzMtLjA1NS4xNzktLjEzNy4yMTctLjMyOS4xNGE1LjUgNS41IDAgMCAxLTEuNzM2LTEuMThjLS44NTctLjgyOC0xLjYzMS0xLjc0Mi0yLjU5Ny0yLjQ1OGExMSAxMSAwIDAgMC0uNjg5LS40NzFjLS45ODUtLjk1Ny4xMy0xLjc0My4zODgtMS44MzYuMjctLjA5OC4wOTMtLjQzMi0uNzc5LS40MjhzLTEuNjcuMjk1LTIuNjg3LjY4NGEzIDMgMCAwIDEtLjQ2NS4xMzcgOS42IDkuNiAwIDAgMC0yLjg4My0uMTAyYy0xLjg4NS4yMS0zLjM5IDEuMTAyLTQuNDk3IDIuNjIzQy4wODIgOC42MDYtLjIzMSAxMC42ODQuMTUyIDEyLjg1Yy40MDMgMi4yODQgMS41NjkgNC4xNzUgMy4zNiA1LjY1MyAxLjg1OCAxLjUzMyAzLjk5NyAyLjI4NCA2LjQzOCAyLjE0IDEuNDgyLS4wODUgMy4xMzMtLjI4NCA0Ljk5NC0xLjg2LjQ3LjIzNC45NjIuMzI3IDEuNzguMzk3LjYzLjA1OSAxLjIzNi0uMDMgMS43MDUtLjEyOC43MzUtLjE1Ni42ODQtLjgzNy40MTktLjk2MS0yLjE1NS0xLjAwNC0xLjY4Mi0uNTk1LTIuMTEzLS45MjYgMS4wOTYtMS4yOTYgMi43NDYtMi42NDIgMy4zOTItNy4wMDMuMDUtLjM0Ny4wMDctLjU2NSAwLS44NDUtLjAwNC0uMTcuMDM1LS4yMzcuMjMtLjI1NmE0LjIgNC4yIDAgMCAwIDEuNTQ1LS40NzVjMS4zOTYtLjc2MyAxLjk2LTIuMDE1IDIuMDkzLTMuNTE3LjAyLS4yMy0uMDA0LS40NjctLjI0Ny0uNTg4ek0xMS41ODEgMThjLTIuMDg5LTEuNjQyLTMuMTAyLTIuMTgzLTMuNTItMi4xNi0uMzkyLjAyNC0uMzIxLjQ3MS0uMjM1Ljc2My4wOS4yODguMjA3LjQ4Ni4zNzEuNzM5LjExNC4xNjcuMTkyLjQxNi0uMTEzLjYwMy0uNjczLjQxNi0xLjg0Mi0uMTQtMS44OTctLjE2Ny0xLjM2MS0uODAyLTIuNS0xLjg2LTMuMzAxLTMuMzA3LS43NzQtMS4zOTMtMS4yMjQtMi44ODctMS4yOTgtNC40ODItLjAyLS4zODYuMDkzLS41MjIuNDc3LS41OTJhNC43IDQuNyAwIDAgMSAxLjUyOS0uMDM5YzIuMTMyLjMxMiAzLjk0NiAxLjI2NSA1LjQ2OCAyLjc3NC44NjguODYgMS41MjUgMS44ODcgMi4yMDIgMi44OTEuNzIgMS4wNjYgMS40OTQgMi4wODIgMi40OCAyLjkxNC4zNDguMjkyLjYyNS41MTQuODkxLjY3Ny0uODAyLjA5LTIuMTQuMTEtMy4wNTQtLjYxNG0xLTYuNDRhLjMwNi4zMDYgMCAwIDEgLjQxNS0uMjg3LjMuMyAwIDAgMSAuMi4yODguMzA2LjMwNiAwIDAgMS0uMzEuMzA3LjMwMy4zMDMgMCAwIDEtLjMwNC0uMzA4em0zLjExIDEuNTk2Yy0uMi4wODEtLjM5OS4xNTEtLjU5LjE2YTEuMjUgMS4yNSAwIDAgMS0uNzk4LS4yNTRjLS4yNzQtLjIzLS40Ny0uMzU4LS41NTItLjc1OGExLjcgMS43IDAgMCAxIC4wMTYtLjU4OGMuMDctLjMyNy0uMDA4LS41MzctLjIzOS0uNzI3LS4xODctLjE1Ni0uNDI2LS4xOTktLjY4OC0uMTk5YS41Ni41NiAwIDAgMS0uMjU0LS4wNzguMjUzLjI1MyAwIDAgMS0uMTE0LS4zNThjLjAyOC0uMDU0LjE2LS4xODYuMTkyLS4yMS4zNTYtLjIwMi43NjctLjEzNiAxLjE0Ni4wMTYuMzUyLjE0NC42MTguNDA4IDEuMDAxLjc4Mi4zOTEuNDUxLjQ2Mi41NzYuNjg1LjkxNC4xNzYuMjY1LjMzNi41MzcuNDQ1Ljg0OC4wNjcuMTk1LS4wMTkuMzU0LS4yNS40NTIiLz48L3N2Zz4=",
|
|
13
12
|
"gemini": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0idXJsKCNhKSIgZD0iTTAgNC4zOTFBNC4zOSA0LjM5IDAgMCAxIDQuMzkxIDBoMTUuMjE3QTQuMzkgNC4zOSAwIDAgMSAyNCA0LjM5MXYxNS4yMTdBNC4zOSA0LjM5IDAgMCAxIDE5LjYwOCAyNEg0LjM5MUE0LjM5IDQuMzkgMCAwIDEgMCAxOS42MDh6Ii8+PHBhdGggZmlsbD0iIzFlMWUyZSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMTkuNzQgMS40NDRhMi44MTYgMi44MTYgMCAwIDEgMi44MTYgMi44MTZ2MTUuNDhhMi44MTYgMi44MTYgMCAwIDEtMi44MTYgMi44MTZINC4yNmEyLjgxNiAyLjgxNiAwIDAgMS0yLjgxNi0yLjgxNlY0LjI2QTIuODE2IDIuODE2IDAgMCAxIDQuMjYgMS40NDR6TTcuMjM2IDguNTY0bDcuNzUyIDMuNzI4LTcuNzUyIDMuNzI3djIuODAybDkuNTU3LTQuNTk2di0zLjg2Nkw3LjIzNiA1Ljc2M3oiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9IjI0IiB4Mj0iMCIgeTE9IjYuNTg3IiB5Mj0iMTYuNDk0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iI2VlNGQ1ZCIvPjxzdG9wIG9mZnNldD0iLjMyOCIgc3RvcC1jb2xvcj0iI2IzODFkZCIvPjxzdG9wIG9mZnNldD0iLjQ3NiIgc3RvcC1jb2xvcj0iIzIwN2NmZSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjwvc3ZnPg==",
|
package/dist/renderers/shiki.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Syntax-highlighted code fragments for artifact rendering. Verbatim port of
|
|
3
|
+
* `apps/web/src/runtime/shiki.ts` in the origin project — no product-specific
|
|
4
|
+
* logic in the original. See `archived provenance ledger`.
|
|
5
|
+
*/
|
|
6
|
+
import { loadOptionalPeer } from '@jini-ai/core/optional-peer';
|
|
1
7
|
let highlighterPromise = null;
|
|
2
8
|
const cache = new Map();
|
|
3
9
|
const CACHE_MAX = 128;
|
|
@@ -8,7 +14,7 @@ const CACHE_MAX = 128;
|
|
|
8
14
|
// around with a cast that would fail at runtime. See `archived provenance ledger`.
|
|
9
15
|
function getHighlighter() {
|
|
10
16
|
if (!highlighterPromise) {
|
|
11
|
-
highlighterPromise = import('shiki/bundle/web').then(({ createHighlighter }) => createHighlighter({
|
|
17
|
+
highlighterPromise = loadOptionalPeer({ feature: 'Syntax highlighting', dependency: 'shiki', load: () => import('shiki/bundle/web') }).then(({ createHighlighter }) => createHighlighter({
|
|
12
18
|
themes: ['github-light-default', 'github-dark-default'],
|
|
13
19
|
langs: [
|
|
14
20
|
'javascript', 'typescript', 'tsx', 'jsx', 'html', 'css', 'json',
|