feat(frontend): add Chrome extension and Claw Skill integration landing pages
Move Chrome extension and Claw Skill entries from the user menu into the integrations center with dedicated landing tabs, setup steps, and lobster-themed Claw styling.
This commit is contained in:
@@ -120,28 +120,6 @@
|
||||
</div>
|
||||
<!-- 切换租户入口在下拉「当前租户」区块 hover;此处仅为分隔线与菜单项。 -->
|
||||
<div class="menu-divider"></div>
|
||||
<div class="menu-item" @click="openClawhubSkill">
|
||||
<span class="menu-icon menu-icon--emoji" role="img" :aria-label="$t('common.clawhubSkill')">🦞</span>
|
||||
<span class="menu-text-with-icon">
|
||||
<span>{{ $t('common.clawhubSkill') }}</span>
|
||||
<span class="menu-new-badge">{{ $t('common.newBadge') }}</span>
|
||||
<svg class="menu-external-icon" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path fill="currentColor"
|
||||
d="M12.667 8a.667.667 0 0 1 .666.667v4a2.667 2.667 0 0 1-2.666 2.666H4.667a2.667 2.667 0 0 1-2.667-2.666V5.333a2.667 2.667 0 0 1 2.667-2.666h4a.667.667 0 1 1 0 1.333h-4a1.333 1.333 0 0 0-1.333 1.333v7.334A1.333 1.333 0 0 0 4.667 13.333h6a1.333 1.333 0 0 0 1.333-1.333v-4A.667.667 0 0 1 12.667 8Zm2.666-6.667v4a.667.667 0 0 1-1.333 0V3.276l-5.195 5.195a.667.667 0 0 1-.943-.943l5.195-5.195h-2.057a.667.667 0 0 1 0-1.333h4a.667.667 0 0 1 .666.666Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="menu-item" @click="openChromeExtension">
|
||||
<t-icon name="extension" class="menu-icon" />
|
||||
<span class="menu-text-with-icon">
|
||||
<span>{{ $t('common.chromeExtension') }}</span>
|
||||
<span class="menu-new-badge">{{ $t('common.newBadge') }}</span>
|
||||
<svg class="menu-external-icon" viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path fill="currentColor"
|
||||
d="M12.667 8a.667.667 0 0 1 .666.667v4a2.667 2.667 0 0 1-2.666 2.666H4.667a2.667 2.667 0 0 1-2.667-2.666V5.333a2.667 2.667 0 0 1 2.667-2.666h4a.667.667 0 1 1 0 1.333h-4a1.333 1.333 0 0 0-1.333 1.333v7.334A1.333 1.333 0 0 0 4.667 13.333h6a1.333 1.333 0 0 0 1.333-1.333v-4A.667.667 0 0 1 12.667 8Zm2.666-6.667v4a.667.667 0 0 1-1.333 0V3.276l-5.195 5.195a.667.667 0 0 1-.943-.943l5.195-5.195h-2.057a.667.667 0 0 1 0-1.333h4a.667.667 0 0 1 .666.666Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="menu-item" :title="$t('common.githubStarTip')" @click="openGithub">
|
||||
<t-icon name="logo-github" class="menu-icon" />
|
||||
<span class="menu-text-with-icon">
|
||||
@@ -522,22 +500,6 @@ const clampFloatingToViewport = (selector: string, target: { value: Record<strin
|
||||
})
|
||||
}
|
||||
|
||||
const CHROME_EXTENSION_URL =
|
||||
'https://chromewebstore.google.com/detail/jpemjbopikggjlmikmclgbmkhhopjdgd?utm_source=item-share-cb'
|
||||
|
||||
const CLAWHUB_SKILL_URL = 'https://clawhub.ai/lyingbug/weknora'
|
||||
|
||||
// 打开 WeKnora Chrome 插件(Chrome应用商店)
|
||||
const openChromeExtension = () => {
|
||||
menuVisible.value = false
|
||||
window.open(CHROME_EXTENSION_URL, '_blank')
|
||||
}
|
||||
|
||||
const openClawhubSkill = () => {
|
||||
menuVisible.value = false
|
||||
window.open(CLAWHUB_SKILL_URL, '_blank')
|
||||
}
|
||||
|
||||
const reopenGuide = () => {
|
||||
menuVisible.value = false
|
||||
openNewUserGuide()
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { getApiBaseUrl } from '@/utils/api-base'
|
||||
|
||||
type WeKnoraDesktopWindow = Window & {
|
||||
__WEKNORA_API_BASE__?: string
|
||||
go?: {
|
||||
main?: {
|
||||
App?: {
|
||||
GetAPIBaseURL?: () => Promise<string> | string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useApiBaseUrlDisplay() {
|
||||
const wailsApiBaseURL = ref<string | null>(null)
|
||||
|
||||
const apiBaseUrlDisplay = computed(() => {
|
||||
if (wailsApiBaseURL.value) {
|
||||
return wailsApiBaseURL.value
|
||||
}
|
||||
const configured = getApiBaseUrl().trim().replace(/\/$/, '')
|
||||
let origin = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
if (!origin || origin === 'null') {
|
||||
origin = ''
|
||||
}
|
||||
const base = configured || origin
|
||||
return `${base}/api/v1`
|
||||
})
|
||||
|
||||
async function loadApiBaseUrl() {
|
||||
const win = window as WeKnoraDesktopWindow
|
||||
for (let i = 0; i < 40; i++) {
|
||||
const injected = win.__WEKNORA_API_BASE__
|
||||
if (typeof injected === 'string' && injected.trim()) {
|
||||
wailsApiBaseURL.value = injected.trim().replace(/\/$/, '')
|
||||
return
|
||||
}
|
||||
const fn = win.go?.main?.App?.GetAPIBaseURL
|
||||
if (typeof fn === 'function') {
|
||||
try {
|
||||
const raw = await Promise.resolve(fn())
|
||||
if (typeof raw === 'string' && raw.trim()) {
|
||||
wailsApiBaseURL.value = raw.trim().replace(/\/$/, '')
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
/* binding error */
|
||||
}
|
||||
}
|
||||
await new Promise<void>((resolve) => setTimeout(resolve, 50))
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadApiBaseUrl()
|
||||
})
|
||||
|
||||
return { apiBaseUrlDisplay }
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const CHROME_EXTENSION_URL =
|
||||
'https://chromewebstore.google.com/detail/jpemjbopikggjlmikmclgbmkhhopjdgd?utm_source=item-share-cb'
|
||||
|
||||
export const CLAWHUB_SKILL_URL = 'https://clawhub.ai/lyingbug/weknora'
|
||||
@@ -5694,10 +5694,12 @@ export default {
|
||||
},
|
||||
integrations: {
|
||||
title: 'Publish & Integrations',
|
||||
subtitle: 'Publish agents to IM platforms and websites',
|
||||
subtitle: 'Publish agents to IM platforms, websites, browsers, and AI assistants',
|
||||
tabs: {
|
||||
im: 'IM Integration',
|
||||
embed: 'Web Embed',
|
||||
chrome: 'Chrome Extension',
|
||||
claw: 'Claw Skill',
|
||||
},
|
||||
manageSectionTitle: 'Channel configuration',
|
||||
manageSectionDesc: 'Select an agent to create or edit integration channels for that agent',
|
||||
@@ -5726,6 +5728,118 @@ export default {
|
||||
desc: 'Publish this agent to IM platforms or websites. Manage in Integrations.',
|
||||
manage: 'Manage',
|
||||
},
|
||||
chrome: {
|
||||
title: 'Knowledge Assistant',
|
||||
subtitle:
|
||||
'For self-hosted WeKnora: ask questions in a sidebar, clip web pages, and save Markdown notes into your knowledge bases while you browse.',
|
||||
capabilitiesTitle: 'Core capabilities',
|
||||
capabilities: {
|
||||
qa: {
|
||||
title: 'Knowledge-base Q&A',
|
||||
desc: 'Sidebar chat with multi-KB switching and fast/deep/precise answer modes—ask without leaving the page.',
|
||||
},
|
||||
clip: {
|
||||
title: 'One-click web capture',
|
||||
desc: 'Save page URLs, AI-extract main content, or manually select regions into a target knowledge base.',
|
||||
},
|
||||
notes: {
|
||||
title: 'Markdown quick notes',
|
||||
desc: 'Built-in Markdown editor for ideas and notes, saved to your knowledge base in one click.',
|
||||
},
|
||||
shortcuts: {
|
||||
title: 'Keyboard shortcuts',
|
||||
desc: 'Customize shortcuts to ask questions, open the sidebar, and speed up daily workflows.',
|
||||
},
|
||||
},
|
||||
scenariosTitle: 'Use cases',
|
||||
scenarios: {
|
||||
research: 'Daily research',
|
||||
learning: 'Study notes',
|
||||
tech: 'Technical references',
|
||||
work: 'Work knowledge',
|
||||
},
|
||||
stepsTitle: 'Setup steps',
|
||||
steps: {
|
||||
api: {
|
||||
title: 'Get API credentials',
|
||||
desc: 'Copy your API Key and base URL from Settings → API Info.',
|
||||
},
|
||||
port: {
|
||||
title: 'Desktop: fixed port (recommended)',
|
||||
desc: 'On WeKnora Desktop, set a fixed API port (e.g. 37841) in API Info so the URL stays stable across restarts.',
|
||||
},
|
||||
install: {
|
||||
title: 'Install the extension',
|
||||
desc: 'Install “Knowledge Assistant” from the Chrome Web Store.',
|
||||
},
|
||||
connect: {
|
||||
title: 'Connect in the extension',
|
||||
desc: 'Open extension settings, choose enterprise/developer mode, and enter the service API URL and API Key. Your current API URL is shown below.',
|
||||
},
|
||||
},
|
||||
openApiSettings: 'Open API Info',
|
||||
copy: 'Copy',
|
||||
copySuccess: 'API URL copied',
|
||||
installCta: 'Chrome Web Store',
|
||||
installCtaHint: 'Official extension · opens in a new tab',
|
||||
storeMeta: 'Chrome Web Store · v1.0.0',
|
||||
},
|
||||
claw: {
|
||||
title: 'WeKnora Skill',
|
||||
subtitle:
|
||||
'Import documents and run hybrid retrieval (vector + keyword) via the WeKnora REST API—for uploads, URL imports, Markdown entries, and cross-KB search.',
|
||||
capabilitiesTitle: 'Skill capabilities',
|
||||
capabilities: {
|
||||
upload: {
|
||||
title: 'Upload files',
|
||||
desc: 'Upload PDF, Word, Excel, and more; automatic parsing and vectorization.',
|
||||
},
|
||||
url: {
|
||||
title: 'Import URLs',
|
||||
desc: 'Fetch web pages by URL into a knowledge base with parse-status polling.',
|
||||
},
|
||||
manual: {
|
||||
title: 'Write Markdown',
|
||||
desc: 'Create or edit knowledge entries as Markdown—ideal for meeting notes.',
|
||||
},
|
||||
search: {
|
||||
title: 'Hybrid search',
|
||||
desc: 'Per-KB hybrid-search and cross-KB knowledge-search combining vector and keyword recall.',
|
||||
},
|
||||
browse: {
|
||||
title: 'Browse knowledge',
|
||||
desc: 'List knowledge bases and entries, view details, and manage imported content.',
|
||||
},
|
||||
},
|
||||
stepsTitle: 'Setup steps',
|
||||
steps: {
|
||||
api: {
|
||||
title: 'Get API credentials',
|
||||
desc: 'Copy API Key and base URL from Settings → API Info.',
|
||||
},
|
||||
env: {
|
||||
title: 'Configure environment',
|
||||
desc: 'Set WEKNORA_BASE_URL and WEKNORA_API_KEY in your shell or ~/.zshrc / ~/.bashrc. The example below uses your current API base URL—replace the API Key with your actual value.',
|
||||
},
|
||||
install: {
|
||||
title: 'Install the skill',
|
||||
desc: 'Run the command below in an environment with the OpenClaw CLI installed, or follow the ClawHub page instructions.',
|
||||
},
|
||||
verify: {
|
||||
title: 'Verify connection',
|
||||
desc: 'Ask the agent to list knowledge bases or run a search to confirm credentials and connectivity.',
|
||||
},
|
||||
},
|
||||
openApiSettings: 'Open API Info',
|
||||
copy: 'Copy',
|
||||
copyEnvSuccess: 'Environment example copied',
|
||||
copyCmdSuccess: 'Install command copied',
|
||||
ecosystemNote:
|
||||
'Skill hosted on ClawHub ({\'@\'}lyingbug/weknora). See the ClawHub page for full API docs and version history.',
|
||||
installCta: 'Open ClawHub',
|
||||
installCtaHint: 'Install WeKnora Skill · opens in a new tab',
|
||||
hubMeta: 'ClawHub · {\'@\'}lyingbug/weknora · MIT-0',
|
||||
},
|
||||
},
|
||||
imOverview: {
|
||||
menuTitle: 'Connected IMs',
|
||||
|
||||
@@ -5705,10 +5705,12 @@ export default {
|
||||
},
|
||||
integrations: {
|
||||
title: "게시 및 통합",
|
||||
subtitle: "에이전트를 IM 플랫폼과 웹사이트에 게시",
|
||||
subtitle: "에이전트를 IM, 웹사이트, 브라우저, AI 어시스턴트에 게시",
|
||||
tabs: {
|
||||
im: "IM 통합",
|
||||
embed: "웹 임베드",
|
||||
chrome: "Chrome 확장",
|
||||
claw: "Claw Skill",
|
||||
},
|
||||
manageSectionTitle: "채널 구성",
|
||||
manageSectionDesc: "에이전트를 선택한 후 해당 에이전트의 통합 채널을 생성하거나 편집할 수 있습니다",
|
||||
@@ -5737,6 +5739,96 @@ export default {
|
||||
desc: "IM 플랫폼 또는 웹사이트에 에이전트를 게시합니다. 통합 센터에서 관리하세요.",
|
||||
manage: "관리",
|
||||
},
|
||||
chrome: {
|
||||
title: "지식 관리 어시스턴트",
|
||||
subtitle:
|
||||
"자체 호스팅 WeKnora와 함께: 사이드바에서 질문하고, 웹 페이지를 클리핑하며, Markdown 메모를 지식베이스에 저장하세요.",
|
||||
capabilitiesTitle: "핵심 기능",
|
||||
capabilities: {
|
||||
qa: {
|
||||
title: "지식베이스 Q&A",
|
||||
desc: "사이드바 대화, 다중 지식베이스 전환, 빠름/심층/정밀 모드로 페이지를 떠나지 않고 질문.",
|
||||
},
|
||||
clip: {
|
||||
title: "원클릭 웹 수집",
|
||||
desc: "URL 저장, AI 본문 추출, 영역 선택으로 지식베이스에 정확히 저장.",
|
||||
},
|
||||
notes: {
|
||||
title: "Markdown 빠른 메모",
|
||||
desc: "내장 Markdown 편집기로 아이디어를 기록하고 한 번에 지식베이스에 저장.",
|
||||
},
|
||||
shortcuts: {
|
||||
title: "단축키",
|
||||
desc: "질문, 사이드바 열기 등 단축키를 사용자 지정해 효율을 높이세요.",
|
||||
},
|
||||
},
|
||||
scenariosTitle: "활용 시나리오",
|
||||
scenarios: {
|
||||
research: "자료 조사",
|
||||
learning: "학습 노트",
|
||||
tech: "기술 자료 수집",
|
||||
work: "업무 지식 축적",
|
||||
},
|
||||
stepsTitle: "구성 단계",
|
||||
steps: {
|
||||
api: {
|
||||
title: "API 자격 증명",
|
||||
desc: "설정 → API 정보에서 API Key와 API 주소를 복사하세요.",
|
||||
},
|
||||
port: {
|
||||
title: "데스크톱: 고정 포트(권장)",
|
||||
desc: "WeKnora 데스크톱에서는 API 정보에서 고정 포트(예: 37841)를 설정하세요.",
|
||||
},
|
||||
install: {
|
||||
title: "Chrome 확장 설치",
|
||||
desc: "Chrome 웹 스토어에서 「지식 관리 어시스턴트」를 설치하세요.",
|
||||
},
|
||||
connect: {
|
||||
title: "확장에서 연결",
|
||||
desc: "확장 설정에서 기업/개발자 모드를 선택하고 서비스 API 주소와 API Key를 입력하세요. 아래는 현재 서비스 주소입니다.",
|
||||
},
|
||||
},
|
||||
openApiSettings: "API 정보 열기",
|
||||
copy: "복사",
|
||||
copySuccess: "API 주소가 복사되었습니다",
|
||||
installCta: "Chrome 웹 스토어",
|
||||
installCtaHint: "공식 확장 · 새 탭에서 열림",
|
||||
storeMeta: "Chrome 웹 스토어 · v1.0.0",
|
||||
},
|
||||
claw: {
|
||||
title: "WeKnora Skill",
|
||||
subtitle:
|
||||
"WeKnora REST API로 문서를 가져오고 하이브리드 검색(벡터+키워드)을 수행합니다. 파일/URL/Markdown 업로드 및 검색에 사용.",
|
||||
capabilitiesTitle: "Skill 기능",
|
||||
capabilities: {
|
||||
upload: { title: "파일 업로드", desc: "PDF, Word, Excel 등을 업로드하고 자동 파싱·벡터화." },
|
||||
url: { title: "URL 가져오기", desc: "URL로 웹 페이지를 지식베이스에 가져오고 파싱 상태를 확인." },
|
||||
manual: { title: "Markdown 작성", desc: "Markdown으로 지식 항목을 생성·편집." },
|
||||
search: { title: "하이브리드 검색", desc: "단일 KB hybrid-search 및 교차 KB knowledge-search." },
|
||||
browse: { title: "지식 탐색", desc: "지식베이스·항목 목록, 상세 보기 및 관리." },
|
||||
},
|
||||
stepsTitle: "구성 단계",
|
||||
steps: {
|
||||
api: { title: "API 자격 증명", desc: "설정 → API 정보에서 API Key와 주소를 복사하세요." },
|
||||
env: {
|
||||
title: "환경 변수 설정",
|
||||
desc: "셸 또는 ~/.zshrc, ~/.bashrc에 WEKNORA_BASE_URL과 WEKNORA_API_KEY를 설정하세요. 아래 예시는 현재 API 주소를 사용하며, API Key는 실제 값으로 바꾸세요.",
|
||||
},
|
||||
install: {
|
||||
title: "Skill 설치",
|
||||
desc: "OpenClaw CLI가 설치된 환경에서 아래 명령을 실행하거나 ClawHub 안내를 따르세요.",
|
||||
},
|
||||
verify: { title: "연결 확인", desc: "에이전트로 지식베이스 목록 또는 검색을 실행해 연결을 확인하세요." },
|
||||
},
|
||||
openApiSettings: "API 정보 열기",
|
||||
copy: "복사",
|
||||
copyEnvSuccess: "환경 변수 예시가 복사되었습니다",
|
||||
copyCmdSuccess: "설치 명령이 복사되었습니다",
|
||||
ecosystemNote: "Skill은 ClawHub({'@'}lyingbug/weknora)에 호스팅됩니다. 전체 API 문서는 ClawHub 페이지를 참고하세요.",
|
||||
installCta: "ClawHub 열기",
|
||||
installCtaHint: "WeKnora Skill 설치 · 새 탭에서 열림",
|
||||
hubMeta: "ClawHub · {'@'}lyingbug/weknora · MIT-0",
|
||||
},
|
||||
},
|
||||
imOverview: {
|
||||
menuTitle: "연결된 IM",
|
||||
|
||||
@@ -5527,10 +5527,12 @@ export default {
|
||||
},
|
||||
integrations: {
|
||||
title: 'Публикация и интеграция',
|
||||
subtitle: 'Публикуйте агентов на IM-платформах и сайтах',
|
||||
subtitle: 'Публикуйте агентов в IM, на сайтах, в браузере и AI-ассистентах',
|
||||
tabs: {
|
||||
im: 'IM-интеграция',
|
||||
embed: 'Веб-встраивание',
|
||||
chrome: 'Расширение Chrome',
|
||||
claw: 'Claw Skill',
|
||||
},
|
||||
manageSectionTitle: 'Настройка каналов',
|
||||
manageSectionDesc: 'Выберите агента, чтобы создать или изменить каналы интеграции для него',
|
||||
@@ -5559,6 +5561,97 @@ export default {
|
||||
desc: 'Публикуйте агента в IM или на сайт. Управление — в разделе «Интеграции».',
|
||||
manage: 'Управлять',
|
||||
},
|
||||
chrome: {
|
||||
title: 'Помощник по знаниям',
|
||||
subtitle:
|
||||
'Для self-hosted WeKnora: вопросы в боковой панели, клиппинг страниц и Markdown-заметки в базы знаний прямо при просмотре.',
|
||||
capabilitiesTitle: 'Возможности',
|
||||
capabilities: {
|
||||
qa: {
|
||||
title: 'Q&A по базе знаний',
|
||||
desc: 'Боковая панель, несколько БЗ, режимы быстрый/глубокий/точный — без смены вкладки.',
|
||||
},
|
||||
clip: {
|
||||
title: 'Сбор веб-контента',
|
||||
desc: 'Сохранение URL, AI-извлечение текста или выбор области на странице.',
|
||||
},
|
||||
notes: {
|
||||
title: 'Markdown-заметки',
|
||||
desc: 'Встроенный редактор Markdown с сохранением в базу знаний в один клик.',
|
||||
},
|
||||
shortcuts: {
|
||||
title: 'Горячие клавиши',
|
||||
desc: 'Настраиваемые сочетания для вопросов, панели и ускорения работы.',
|
||||
},
|
||||
},
|
||||
scenariosTitle: 'Сценарии',
|
||||
scenarios: {
|
||||
research: 'Исследования',
|
||||
learning: 'Учёба',
|
||||
tech: 'Техдокументация',
|
||||
work: 'Рабочие знания',
|
||||
},
|
||||
stepsTitle: 'Шаги настройки',
|
||||
steps: {
|
||||
api: {
|
||||
title: 'Получите API-учётные данные',
|
||||
desc: 'Скопируйте API Key и базовый URL в «Настройки → API-информация».',
|
||||
},
|
||||
port: {
|
||||
title: 'Десктоп: фиксированный порт',
|
||||
desc: 'В WeKnora Desktop задайте фиксированный порт API (например 37841) в API-информации.',
|
||||
},
|
||||
install: {
|
||||
title: 'Установите расширение',
|
||||
desc: 'Установите «Помощник по знаниям» из Chrome Web Store.',
|
||||
},
|
||||
connect: {
|
||||
title: 'Подключите в расширении',
|
||||
desc: 'В настройках расширения выберите режим enterprise/developer и введите API URL и API Key. Ниже — текущий URL сервиса.',
|
||||
},
|
||||
},
|
||||
openApiSettings: 'Открыть API-информацию',
|
||||
copy: 'Копировать',
|
||||
copySuccess: 'URL API скопирован',
|
||||
installCta: 'Chrome Web Store',
|
||||
installCtaHint: 'Официальное расширение · откроется в новой вкладке',
|
||||
storeMeta: 'Chrome Web Store · v1.0.0',
|
||||
},
|
||||
claw: {
|
||||
title: 'WeKnora Skill',
|
||||
subtitle:
|
||||
'Импорт документов и гибридный поиск (вектор + ключевые слова) через REST API WeKnora — загрузки, URL, Markdown и поиск.',
|
||||
capabilitiesTitle: 'Возможности Skill',
|
||||
capabilities: {
|
||||
upload: { title: 'Загрузка файлов', desc: 'PDF, Word, Excel и др. с автоматическим разбором.' },
|
||||
url: { title: 'Импорт URL', desc: 'Загрузка веб-страниц по URL с отслеживанием parse_status.' },
|
||||
manual: { title: 'Markdown', desc: 'Создание и правка записей в формате Markdown.' },
|
||||
search: { title: 'Гибридный поиск', desc: 'hybrid-search по одной БЗ и knowledge-search по нескольким.' },
|
||||
browse: { title: 'Просмотр знаний', desc: 'Списки БЗ и записей, детали и управление контентом.' },
|
||||
},
|
||||
stepsTitle: 'Шаги настройки',
|
||||
steps: {
|
||||
api: { title: 'API-учётные данные', desc: 'Скопируйте API Key и URL в «Настройки → API-информация».' },
|
||||
env: {
|
||||
title: 'Переменные окружения',
|
||||
desc: 'Задайте WEKNORA_BASE_URL и WEKNORA_API_KEY в shell или ~/.zshrc / ~/.bashrc. Пример ниже использует текущий API URL — замените API Key на фактическое значение.',
|
||||
},
|
||||
install: {
|
||||
title: 'Установка Skill',
|
||||
desc: 'Выполните команду ниже в среде с OpenClaw CLI или следуйте инструкциям на ClawHub.',
|
||||
},
|
||||
verify: { title: 'Проверка', desc: 'Попросите агента вывести список БЗ или выполнить поиск.' },
|
||||
},
|
||||
openApiSettings: 'Открыть API-информацию',
|
||||
copy: 'Копировать',
|
||||
copyEnvSuccess: 'Пример переменных скопирован',
|
||||
copyCmdSuccess: 'Команда установки скопирована',
|
||||
ecosystemNote:
|
||||
'Skill размещён на ClawHub ({\'@\'}lyingbug/weknora). Полная документация API — на странице ClawHub.',
|
||||
installCta: 'Открыть ClawHub',
|
||||
installCtaHint: 'Установка WeKnora Skill · откроется в новой вкладке',
|
||||
hubMeta: 'ClawHub · {\'@\'}lyingbug/weknora · MIT-0',
|
||||
},
|
||||
},
|
||||
imOverview: {
|
||||
menuTitle: 'Подключённые IM',
|
||||
|
||||
@@ -5712,10 +5712,12 @@ export default {
|
||||
},
|
||||
integrations: {
|
||||
title: "发布集成",
|
||||
subtitle: "将智能体发布到 IM 平台与网站",
|
||||
subtitle: "将智能体发布到 IM、网站、浏览器与 AI 助手",
|
||||
tabs: {
|
||||
im: "IM 集成",
|
||||
embed: "网页嵌入",
|
||||
chrome: "Chrome 插件",
|
||||
claw: "Claw Skill",
|
||||
},
|
||||
manageSectionTitle: "渠道配置",
|
||||
manageSectionDesc: "选择智能体后,可创建或编辑该智能体下的集成渠道",
|
||||
@@ -5744,6 +5746,118 @@ export default {
|
||||
desc: "将智能体发布到 IM 平台或网站,在集成中心统一管理",
|
||||
manage: "管理",
|
||||
},
|
||||
chrome: {
|
||||
title: "知识管理助手",
|
||||
subtitle:
|
||||
"配合 WeKnora 自建服务使用:在任意网页侧边栏提问、剪藏内容、Markdown 速记,将浏览中的知识沉淀到你的知识库。",
|
||||
capabilitiesTitle: "核心能力",
|
||||
capabilities: {
|
||||
qa: {
|
||||
title: "知识库智能问答",
|
||||
desc: "侧边栏对话面板,支持多知识库切换与快速/深度/精确三种回答模式,边浏览边提问不打断工作流。",
|
||||
},
|
||||
clip: {
|
||||
title: "网页内容一键采集",
|
||||
desc: "保存页面 URL、AI 智能剪藏正文,或手动框选区域,精准写入指定知识库。",
|
||||
},
|
||||
notes: {
|
||||
title: "Markdown 速记",
|
||||
desc: "内置 Markdown 编辑器,随时记录灵感与笔记,一键保存到知识库。",
|
||||
},
|
||||
shortcuts: {
|
||||
title: "高效快捷键",
|
||||
desc: "可自定义快捷键快速提问、打开侧边栏等操作,提升日常效率。",
|
||||
},
|
||||
},
|
||||
scenariosTitle: "适用场景",
|
||||
scenarios: {
|
||||
research: "日常资料调研",
|
||||
learning: "学习笔记整理",
|
||||
tech: "技术资料收集",
|
||||
work: "工作知识沉淀",
|
||||
},
|
||||
stepsTitle: "配置步骤",
|
||||
steps: {
|
||||
api: {
|
||||
title: "获取 API 凭证",
|
||||
desc: "在「设置 → API 信息」中复制 API Key 与 API 地址。",
|
||||
},
|
||||
port: {
|
||||
title: "桌面版配置固定端口(推荐)",
|
||||
desc: "使用 WeKnora 桌面版时,在 API 信息中设置固定端口(如 37841),避免每次启动后地址变化导致插件断连。",
|
||||
},
|
||||
install: {
|
||||
title: "安装 Chrome 插件",
|
||||
desc: "前往 Chrome 应用商店安装「知识管理助手」。",
|
||||
},
|
||||
connect: {
|
||||
title: "在插件中完成连接",
|
||||
desc: "打开插件设置,选择「企业/开发者」模式,填入服务 API 地址与 API Key。下方为当前服务地址。",
|
||||
},
|
||||
},
|
||||
openApiSettings: "打开 API 信息",
|
||||
copy: "复制",
|
||||
copySuccess: "已复制 API 地址",
|
||||
installCta: "前往 Chrome 应用商店",
|
||||
installCtaHint: "官方扩展 · 将在新标签页打开",
|
||||
storeMeta: "Chrome 应用商店 · v1.0.0",
|
||||
},
|
||||
claw: {
|
||||
title: "WeKnora Skill",
|
||||
subtitle:
|
||||
"通过 WeKnora REST API 导入文档并执行混合检索(向量 + 关键词)。适用于上传文件/URL/Markdown 到知识库、跨库检索与浏览知识内容。",
|
||||
capabilitiesTitle: "Skill 能力",
|
||||
capabilities: {
|
||||
upload: {
|
||||
title: "上传文件",
|
||||
desc: "将 PDF、Word、Excel 等文档上传至知识库,自动解析与向量化。",
|
||||
},
|
||||
url: {
|
||||
title: "导入网页",
|
||||
desc: "通过 URL 抓取网页内容并写入知识库,支持解析状态轮询。",
|
||||
},
|
||||
manual: {
|
||||
title: "写入 Markdown",
|
||||
desc: "以 Markdown 形式创建或编辑知识条目,适合会议记录与结构化笔记。",
|
||||
},
|
||||
search: {
|
||||
title: "混合检索",
|
||||
desc: "单库 hybrid-search 与跨库 knowledge-search,结合向量与关键词召回。",
|
||||
},
|
||||
browse: {
|
||||
title: "浏览知识库",
|
||||
desc: "列出知识库与条目、查看详情,管理已导入的知识内容。",
|
||||
},
|
||||
},
|
||||
stepsTitle: "配置步骤",
|
||||
steps: {
|
||||
api: {
|
||||
title: "获取 API 凭证",
|
||||
desc: "在「设置 → API 信息」中复制 API Key 与 API 地址。",
|
||||
},
|
||||
env: {
|
||||
title: "配置环境变量",
|
||||
desc: "在终端或 ~/.zshrc、~/.bashrc 中设置 WEKNORA_BASE_URL 与 WEKNORA_API_KEY。下方示例已填入当前 API 地址,请将 API Key 替换为实际值。",
|
||||
},
|
||||
install: {
|
||||
title: "安装 Skill",
|
||||
desc: "在已安装 OpenClaw CLI 的环境中执行下方命令,或前往 ClawHub 页面按指引安装。",
|
||||
},
|
||||
verify: {
|
||||
title: "验证连接",
|
||||
desc: "安装后让 Agent 列出知识库或执行一次检索,确认 API 凭证与网络可达。",
|
||||
},
|
||||
},
|
||||
openApiSettings: "打开 API 信息",
|
||||
copy: "复制",
|
||||
copyEnvSuccess: "已复制环境变量示例",
|
||||
copyCmdSuccess: "已复制安装命令",
|
||||
ecosystemNote:
|
||||
"Skill 托管于 ClawHub({'@'}lyingbug/weknora),完整 API 说明与版本历史请参见 ClawHub 页面。",
|
||||
installCta: "前往 ClawHub",
|
||||
installCtaHint: "安装 WeKnora Skill · 将在新标签页打开",
|
||||
hubMeta: "ClawHub · {'@'}lyingbug/weknora · MIT-0",
|
||||
},
|
||||
},
|
||||
imOverview: {
|
||||
menuTitle: "已接入的 IM",
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<IntegrationLandingLayout
|
||||
:title="$t('integrations.chrome.title')"
|
||||
:subtitle="$t('integrations.chrome.subtitle')"
|
||||
variant="chrome"
|
||||
>
|
||||
<template #icon>
|
||||
<t-icon name="extension" size="22px" />
|
||||
</template>
|
||||
|
||||
<template #tags>
|
||||
<span v-for="key in scenarioKeys" :key="key" class="scenario-tag">
|
||||
{{ $t(`integrations.chrome.scenarios.${key}`) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<IntegrationExternalCta
|
||||
variant="chrome"
|
||||
:label="$t('integrations.chrome.installCta')"
|
||||
:hint="$t('integrations.chrome.installCtaHint')"
|
||||
@click="openChromeStore"
|
||||
>
|
||||
<template #icon>
|
||||
<t-icon name="extension" size="18px" />
|
||||
</template>
|
||||
</IntegrationExternalCta>
|
||||
</template>
|
||||
|
||||
<template #main>
|
||||
<div class="landing-group">
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">
|
||||
{{ $t('integrations.chrome.capabilitiesTitle') }}
|
||||
<span class="section-head-extra">{{ capabilityKeys.length }}</span>
|
||||
</h4>
|
||||
<div class="capability-grid">
|
||||
<div v-for="key in capabilityKeys" :key="key" class="capability-card">
|
||||
<div class="capability-card__icon">
|
||||
<t-icon :name="capabilityIcons[key]" />
|
||||
</div>
|
||||
<h5 class="capability-card__title">{{ $t(`integrations.chrome.capabilities.${key}.title`) }}</h5>
|
||||
<p class="capability-card__desc">{{ $t(`integrations.chrome.capabilities.${key}.desc`) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #aside>
|
||||
<div class="landing-group">
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">{{ $t('integrations.chrome.stepsTitle') }}</h4>
|
||||
<ol class="landing-steps">
|
||||
<li v-for="(step, index) in stepKeys" :key="step" class="landing-step">
|
||||
<span class="landing-step-num">{{ index + 1 }}</span>
|
||||
<div class="landing-step-body">
|
||||
<div class="landing-step-title">{{ $t(`integrations.chrome.steps.${step}.title`) }}</div>
|
||||
<p class="landing-step-desc">{{ $t(`integrations.chrome.steps.${step}.desc`) }}</p>
|
||||
<t-button
|
||||
v-if="step === 'api'"
|
||||
size="small"
|
||||
variant="outline"
|
||||
class="landing-step-action"
|
||||
@click="openApiSettings"
|
||||
>
|
||||
{{ $t('integrations.chrome.openApiSettings') }}
|
||||
</t-button>
|
||||
<div v-if="step === 'connect'" class="landing-step-embed credential-row">
|
||||
<div class="api-key-control landing-api-control">
|
||||
<t-input :model-value="apiBaseUrlDisplay" readonly class="mono-text-input" />
|
||||
<t-button
|
||||
size="small"
|
||||
variant="text"
|
||||
:title="$t('integrations.chrome.copy')"
|
||||
@click="copyApiUrl"
|
||||
>
|
||||
<t-icon name="file-copy" size="16px" />
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<span class="landing-meta">{{ $t('integrations.chrome.storeMeta') }}</span>
|
||||
</template>
|
||||
</IntegrationLandingLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { MessagePlugin } from 'tdesign-vue-next'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { CHROME_EXTENSION_URL } from '@/config/integrations'
|
||||
import { useApiBaseUrlDisplay } from '@/composables/useApiBaseUrlDisplay'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import IntegrationLandingLayout from './IntegrationLandingLayout.vue'
|
||||
import IntegrationExternalCta from './IntegrationExternalCta.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const uiStore = useUIStore()
|
||||
const { apiBaseUrlDisplay } = useApiBaseUrlDisplay()
|
||||
|
||||
const capabilityKeys = ['qa', 'clip', 'notes', 'shortcuts'] as const
|
||||
const scenarioKeys = ['research', 'learning', 'tech', 'work'] as const
|
||||
const stepKeys = ['api', 'port', 'install', 'connect'] as const
|
||||
|
||||
const capabilityIcons: Record<(typeof capabilityKeys)[number], string> = {
|
||||
qa: 'chat-bubble',
|
||||
clip: 'file-copy',
|
||||
notes: 'edit',
|
||||
shortcuts: 'jump',
|
||||
}
|
||||
|
||||
const openChromeStore = () => {
|
||||
window.open(CHROME_EXTENSION_URL, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
const openApiSettings = () => {
|
||||
router.push('/platform/knowledge-bases')
|
||||
uiStore.openSettings('api')
|
||||
}
|
||||
|
||||
const copyApiUrl = async () => {
|
||||
const text = apiBaseUrlDisplay.value
|
||||
if (!text) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
MessagePlugin.success(t('integrations.chrome.copySuccess'))
|
||||
} catch {
|
||||
MessagePlugin.success(t('integrations.chrome.copySuccess'))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<IntegrationLandingLayout
|
||||
:title="$t('integrations.claw.title')"
|
||||
:subtitle="$t('integrations.claw.subtitle')"
|
||||
variant="claw"
|
||||
>
|
||||
<template #icon>
|
||||
<span role="img" :aria-label="$t('common.clawhubSkill')">🦞</span>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<IntegrationExternalCta
|
||||
variant="claw"
|
||||
:label="$t('integrations.claw.installCta')"
|
||||
:hint="$t('integrations.claw.installCtaHint')"
|
||||
@click="openClawHub"
|
||||
>
|
||||
<template #icon>
|
||||
<span class="ext-cta-emoji" role="img" :aria-label="$t('common.clawhubSkill')">🦞</span>
|
||||
</template>
|
||||
</IntegrationExternalCta>
|
||||
</template>
|
||||
|
||||
<template #main>
|
||||
<div class="landing-group">
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">
|
||||
{{ $t('integrations.claw.capabilitiesTitle') }}
|
||||
<span class="section-head-extra">{{ capabilityKeys.length }}</span>
|
||||
</h4>
|
||||
<div class="capability-grid capability-grid--claw">
|
||||
<div v-for="key in capabilityKeys" :key="key" class="capability-card">
|
||||
<div class="capability-card__icon">
|
||||
<t-icon :name="capabilityIcons[key]" />
|
||||
</div>
|
||||
<h5 class="capability-card__title">{{ $t(`integrations.claw.capabilities.${key}.title`) }}</h5>
|
||||
<p class="capability-card__desc">{{ $t(`integrations.claw.capabilities.${key}.desc`) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #aside>
|
||||
<div class="landing-group">
|
||||
<section class="setting-drawer__section">
|
||||
<h4 class="setting-drawer__section-title">{{ $t('integrations.claw.stepsTitle') }}</h4>
|
||||
<ol class="landing-steps">
|
||||
<li v-for="(step, index) in stepKeys" :key="step" class="landing-step">
|
||||
<span class="landing-step-num">{{ index + 1 }}</span>
|
||||
<div class="landing-step-body">
|
||||
<div class="landing-step-title">{{ $t(`integrations.claw.steps.${step}.title`) }}</div>
|
||||
<p class="landing-step-desc">{{ $t(`integrations.claw.steps.${step}.desc`) }}</p>
|
||||
<t-button
|
||||
v-if="step === 'api'"
|
||||
size="small"
|
||||
variant="outline"
|
||||
class="landing-step-action"
|
||||
@click="openApiSettings"
|
||||
>
|
||||
{{ $t('integrations.claw.openApiSettings') }}
|
||||
</t-button>
|
||||
<div v-if="step === 'env'" class="landing-step-embed">
|
||||
<div class="code-toolbar">
|
||||
<pre class="code-toolbar__code">{{ envExample }}</pre>
|
||||
<t-button
|
||||
class="code-toolbar__copy"
|
||||
size="small"
|
||||
variant="text"
|
||||
shape="square"
|
||||
:title="$t('integrations.claw.copy')"
|
||||
@click="copyEnvExample"
|
||||
>
|
||||
<t-icon name="file-copy" size="16px" />
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="step === 'install'" class="landing-step-embed">
|
||||
<div class="code-toolbar">
|
||||
<pre class="code-toolbar__code">{{ installCommand }}</pre>
|
||||
<t-button
|
||||
class="code-toolbar__copy"
|
||||
size="small"
|
||||
variant="text"
|
||||
shape="square"
|
||||
:title="$t('integrations.claw.copy')"
|
||||
@click="copyInstallCommand"
|
||||
>
|
||||
<t-icon name="file-copy" size="16px" />
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="landing-footer-bar" role="note">
|
||||
<p class="landing-footer-bar__note">{{ $t('integrations.claw.ecosystemNote') }}</p>
|
||||
<span class="landing-meta">{{ $t('integrations.claw.hubMeta') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</IntegrationLandingLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { MessagePlugin } from 'tdesign-vue-next'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { CLAWHUB_SKILL_URL } from '@/config/integrations'
|
||||
import { useApiBaseUrlDisplay } from '@/composables/useApiBaseUrlDisplay'
|
||||
import { useUIStore } from '@/stores/ui'
|
||||
import IntegrationLandingLayout from './IntegrationLandingLayout.vue'
|
||||
import IntegrationExternalCta from './IntegrationExternalCta.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const uiStore = useUIStore()
|
||||
const { apiBaseUrlDisplay } = useApiBaseUrlDisplay()
|
||||
|
||||
const capabilityKeys = ['upload', 'url', 'manual', 'search', 'browse'] as const
|
||||
const stepKeys = ['api', 'env', 'install', 'verify'] as const
|
||||
|
||||
const capabilityIcons: Record<(typeof capabilityKeys)[number], string> = {
|
||||
upload: 'upload',
|
||||
url: 'link',
|
||||
manual: 'edit',
|
||||
search: 'search',
|
||||
browse: 'view-list',
|
||||
}
|
||||
|
||||
const installCommand = 'openclaw skills install @lyingbug/weknora'
|
||||
|
||||
const envExample = computed(() => {
|
||||
const base = apiBaseUrlDisplay.value || 'https://your-server.com/api/v1'
|
||||
return `export WEKNORA_BASE_URL="${base}"\nexport WEKNORA_API_KEY="sk-your-api-key"`
|
||||
})
|
||||
|
||||
const openClawHub = () => {
|
||||
window.open(CLAWHUB_SKILL_URL, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
const openApiSettings = () => {
|
||||
router.push('/platform/knowledge-bases')
|
||||
uiStore.openSettings('api')
|
||||
}
|
||||
|
||||
const copyText = async (text: string, successKey: string) => {
|
||||
if (!text) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(text)
|
||||
MessagePlugin.success(t(successKey))
|
||||
} catch {
|
||||
MessagePlugin.success(t(successKey))
|
||||
}
|
||||
}
|
||||
|
||||
const copyEnvExample = () => copyText(envExample.value, 'integrations.claw.copyEnvSuccess')
|
||||
const copyInstallCommand = () => copyText(installCommand, 'integrations.claw.copyCmdSuccess')
|
||||
</script>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<button
|
||||
type="button"
|
||||
class="ext-cta"
|
||||
:class="`ext-cta--${variant}`"
|
||||
@click="emit('click')"
|
||||
>
|
||||
<span class="ext-cta__badge" aria-hidden="true">
|
||||
<slot name="icon" />
|
||||
</span>
|
||||
<span class="ext-cta__body">
|
||||
<span class="ext-cta__label">{{ label }}</span>
|
||||
<span v-if="hint" class="ext-cta__hint">{{ hint }}</span>
|
||||
</span>
|
||||
<span class="ext-cta__arrow-wrap" aria-hidden="true">
|
||||
<t-icon name="jump" class="ext-cta__arrow" />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
label: string
|
||||
hint?: string
|
||||
variant?: 'chrome' | 'claw'
|
||||
}>(),
|
||||
{ variant: 'chrome' },
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
click: []
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="integration-landing" :class="{ 'integration-landing--claw': variant === 'claw' }">
|
||||
<header class="landing-hero" :class="{ 'landing-hero--claw': variant === 'claw' }">
|
||||
<div class="landing-hero__icon" :class="`landing-hero__icon--${variant}`">
|
||||
<slot name="icon" />
|
||||
</div>
|
||||
<div class="landing-hero__content">
|
||||
<h2 class="landing-hero__title">{{ title }}</h2>
|
||||
<p v-if="subtitle" class="landing-hero__subtitle">{{ subtitle }}</p>
|
||||
<div v-if="$slots.tags" class="landing-hero__tags">
|
||||
<slot name="tags" />
|
||||
</div>
|
||||
<div v-if="$slots.actions" class="landing-hero__actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="landing-body">
|
||||
<section class="landing-main">
|
||||
<slot name="main" />
|
||||
</section>
|
||||
<aside class="landing-aside">
|
||||
<slot name="aside" />
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<footer v-if="$slots.footer" class="landing-footer">
|
||||
<slot name="footer" />
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title: string
|
||||
subtitle?: string
|
||||
variant?: 'chrome' | 'claw'
|
||||
}>(),
|
||||
{ variant: 'chrome' },
|
||||
)
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@import './integration-landing.less';
|
||||
</style>
|
||||
@@ -3,12 +3,6 @@
|
||||
<Transition name="modal">
|
||||
<div v-if="visible" class="settings-overlay" @click.self="handleClose">
|
||||
<div class="settings-modal">
|
||||
<button class="close-btn" @click="handleClose" :aria-label="$t('common.close')">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M15 5L5 15M5 5L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="settings-container">
|
||||
<div class="settings-sidebar">
|
||||
<div class="sidebar-header">
|
||||
@@ -22,14 +16,20 @@
|
||||
:class="['nav-item', { active: currentSection === item.key }]"
|
||||
@click="currentSection = item.key"
|
||||
>
|
||||
<t-icon :name="item.icon" class="nav-icon" />
|
||||
<span v-if="item.emoji" class="nav-emoji" role="img" :aria-label="item.label">{{ item.emoji }}</span>
|
||||
<t-icon v-else :name="item.icon" class="nav-icon" />
|
||||
<span class="nav-label">{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-content">
|
||||
<div class="content-wrapper">
|
||||
<button class="close-btn" @click="handleClose" :aria-label="$t('common.close')">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M15 5L5 15M5 5L15 15" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="content-wrapper" :class="{ 'content-wrapper--landing': isLandingSection }">
|
||||
<div v-if="currentSection === 'im'" class="section">
|
||||
<div class="section-header">
|
||||
<h2>{{ $t('agentEditor.im.title') }}</h2>
|
||||
@@ -56,6 +56,9 @@
|
||||
</div>
|
||||
<AgentEmbedChannelPanel v-model:filter-agent-id="filterAgentId" />
|
||||
</div>
|
||||
|
||||
<ChromeExtensionLanding v-if="currentSection === 'chrome'" />
|
||||
<ClawSkillLanding v-if="currentSection === 'claw'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,25 +74,37 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import IMChannelPanel from '@/components/IMChannelPanel.vue';
|
||||
import AgentEmbedChannelPanel from '@/components/AgentEmbedChannelPanel.vue';
|
||||
import ChromeExtensionLanding from '@/views/integrations/ChromeExtensionLanding.vue';
|
||||
import ClawSkillLanding from '@/views/integrations/ClawSkillLanding.vue';
|
||||
|
||||
type IntegrationTab = 'im' | 'embed' | 'chrome' | 'claw';
|
||||
|
||||
const INTEGRATION_TABS: IntegrationTab[] = ['im', 'embed', 'chrome', 'claw'];
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const currentSection = ref<'im' | 'embed'>('im');
|
||||
const currentSection = ref<IntegrationTab>('im');
|
||||
const filterAgentId = ref('');
|
||||
|
||||
const visible = computed(() => route.name === 'integrations');
|
||||
|
||||
const isLandingSection = computed(
|
||||
() => currentSection.value === 'chrome' || currentSection.value === 'claw',
|
||||
);
|
||||
|
||||
const navItems = computed(() => [
|
||||
{ key: 'im' as const, icon: 'chat-message', label: t('integrations.tabs.im') },
|
||||
{ key: 'embed' as const, icon: 'code', label: t('integrations.tabs.embed') },
|
||||
{ key: 'chrome' as const, icon: 'extension', label: t('integrations.tabs.chrome') },
|
||||
{ key: 'claw' as const, icon: '', emoji: '🦞', label: t('integrations.tabs.claw') },
|
||||
]);
|
||||
|
||||
function applyRouteQuery() {
|
||||
const tab = route.query.tab as string;
|
||||
if (tab === 'im' || tab === 'embed') {
|
||||
currentSection.value = tab;
|
||||
if (INTEGRATION_TABS.includes(tab as IntegrationTab)) {
|
||||
currentSection.value = tab as IntegrationTab;
|
||||
}
|
||||
filterAgentId.value = (route.query.agentId as string) || '';
|
||||
}
|
||||
@@ -157,14 +172,23 @@ watch(
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
background: var(--td-bg-color-container);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@@ -173,6 +197,7 @@ watch(
|
||||
color: var(--td-text-color-secondary);
|
||||
transition: all 0.2s ease;
|
||||
z-index: 10;
|
||||
box-shadow: 0 0 0 1px var(--td-component-stroke);
|
||||
|
||||
&:hover {
|
||||
background: var(--td-bg-color-container-hover);
|
||||
@@ -180,6 +205,18 @@ watch(
|
||||
}
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 24px 28px 28px;
|
||||
|
||||
&--landing {
|
||||
padding-top: 44px;
|
||||
padding-right: 52px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
@@ -257,6 +294,15 @@ watch(
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.nav-emoji {
|
||||
margin-right: 8px;
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -265,20 +311,6 @@ watch(
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 24px 28px 28px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
margin-bottom: 20px;
|
||||
|
||||
|
||||
@@ -0,0 +1,666 @@
|
||||
@claw-accent: #e85d2a;
|
||||
@claw-accent-dark: #c44d1f;
|
||||
|
||||
.integration-landing {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
|
||||
// TDesign 按钮内 icon / 文案垂直居中(落地页局部修正)
|
||||
:deep(.t-button) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
:deep(.t-button .t-icon) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Hero ──
|
||||
.landing-hero {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
padding: 16px 18px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--td-component-stroke);
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
color-mix(in srgb, var(--td-brand-color) 10%, var(--td-bg-color-container)) 0%,
|
||||
color-mix(in srgb, var(--td-brand-color) 4%, var(--td-bg-color-container)) 42%,
|
||||
var(--td-bg-color-container) 72%
|
||||
);
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--td-brand-color) 8%, transparent);
|
||||
|
||||
&--claw {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
color-mix(in srgb, @claw-accent 12%, var(--td-bg-color-container)) 0%,
|
||||
color-mix(in srgb, @claw-accent 5%, var(--td-bg-color-container)) 45%,
|
||||
var(--td-bg-color-container) 72%
|
||||
);
|
||||
box-shadow: inset 0 1px 0 color-mix(in srgb, @claw-accent 10%, transparent);
|
||||
|
||||
.landing-hero__icon {
|
||||
background: color-mix(in srgb, @claw-accent 14%, var(--td-bg-color-container));
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
flex-shrink: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--td-brand-color) 14%, var(--td-bg-color-container));
|
||||
color: var(--td-brand-color);
|
||||
font-size: 22px;
|
||||
line-height: 1;
|
||||
|
||||
:deep(.t-icon) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0 0 4px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
|
||||
&__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
margin-top: 14px;
|
||||
align-self: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.ext-cta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
min-height: 52px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px dashed color-mix(in srgb, var(--td-brand-color) 28%, var(--td-component-stroke));
|
||||
background: color-mix(in srgb, var(--td-bg-color-container) 55%, transparent);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
box-sizing: border-box;
|
||||
transition:
|
||||
border-color 0.18s ease,
|
||||
border-style 0.18s ease,
|
||||
background 0.18s ease,
|
||||
box-shadow 0.18s ease;
|
||||
|
||||
&:hover {
|
||||
border-style: solid;
|
||||
border-color: color-mix(in srgb, var(--td-brand-color) 40%, var(--td-component-stroke));
|
||||
background: color-mix(in srgb, var(--td-bg-color-container) 92%, transparent);
|
||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--td-brand-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&--claw {
|
||||
border-color: color-mix(in srgb, @claw-accent 30%, var(--td-component-stroke));
|
||||
|
||||
&:hover {
|
||||
border-color: color-mix(in srgb, @claw-accent 45%, var(--td-component-stroke));
|
||||
}
|
||||
|
||||
.ext-cta__badge {
|
||||
background: color-mix(in srgb, @claw-accent 12%, var(--td-bg-color-container));
|
||||
color: @claw-accent-dark;
|
||||
}
|
||||
|
||||
.ext-cta__arrow-wrap {
|
||||
background: color-mix(in srgb, @claw-accent 10%, transparent);
|
||||
color: @claw-accent-dark;
|
||||
}
|
||||
|
||||
&:hover .ext-cta__arrow-wrap {
|
||||
background: color-mix(in srgb, @claw-accent 16%, transparent);
|
||||
color: @claw-accent-dark;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline-color: @claw-accent;
|
||||
}
|
||||
}
|
||||
|
||||
&__badge {
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--td-brand-color) 10%, var(--td-bg-color-container));
|
||||
color: var(--td-brand-color);
|
||||
line-height: 0;
|
||||
|
||||
:deep(.t-icon) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
&__hint {
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
&__arrow-wrap {
|
||||
flex-shrink: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 7px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: color-mix(in srgb, var(--td-brand-color) 8%, transparent);
|
||||
color: var(--td-text-color-secondary);
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
|
||||
&__arrow {
|
||||
font-size: 15px;
|
||||
line-height: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&:hover &__arrow-wrap {
|
||||
background: color-mix(in srgb, var(--td-brand-color) 14%, transparent);
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
}
|
||||
|
||||
.ext-cta-emoji {
|
||||
font-size: 17px;
|
||||
line-height: 1;
|
||||
display: block;
|
||||
}
|
||||
|
||||
// ── Claw / 龙虾主题色 ──
|
||||
.integration-landing--claw {
|
||||
.landing-hero__icon--claw {
|
||||
color: @claw-accent-dark;
|
||||
background: color-mix(in srgb, @claw-accent 14%, var(--td-bg-color-container));
|
||||
}
|
||||
|
||||
.landing-group {
|
||||
.setting-drawer__section-title::before {
|
||||
background: @claw-accent;
|
||||
}
|
||||
|
||||
.section-head-extra {
|
||||
color: @claw-accent-dark;
|
||||
background: color-mix(in srgb, @claw-accent 12%, var(--td-bg-color-secondarycontainer));
|
||||
}
|
||||
}
|
||||
|
||||
.capability-card__icon {
|
||||
color: @claw-accent-dark;
|
||||
background: color-mix(in srgb, @claw-accent 10%, var(--td-bg-color-container));
|
||||
}
|
||||
|
||||
.landing-step-num {
|
||||
background: color-mix(in srgb, @claw-accent 14%, transparent);
|
||||
color: @claw-accent-dark;
|
||||
}
|
||||
|
||||
.landing-footer-bar {
|
||||
border-color: color-mix(in srgb, @claw-accent 22%, var(--td-component-stroke));
|
||||
background: color-mix(in srgb, @claw-accent 6%, var(--td-bg-color-secondarycontainer));
|
||||
}
|
||||
|
||||
.landing-meta {
|
||||
color: color-mix(in srgb, @claw-accent-dark 55%, var(--td-text-color-placeholder));
|
||||
}
|
||||
|
||||
.ext-cta--claw:hover .ext-cta__arrow-wrap {
|
||||
color: @claw-accent-dark;
|
||||
}
|
||||
|
||||
:deep(.t-button--variant-outline) {
|
||||
color: @claw-accent-dark;
|
||||
border-color: color-mix(in srgb, @claw-accent 38%, var(--td-component-border));
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: @claw-accent-dark;
|
||||
border-color: @claw-accent;
|
||||
background-color: color-mix(in srgb, @claw-accent 8%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.t-button--variant-text) {
|
||||
color: @claw-accent-dark;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
color: @claw-accent-dark;
|
||||
background-color: color-mix(in srgb, @claw-accent 10%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.t-button--variant-text .t-icon) {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.scenario-tag {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
line-height: 1.45;
|
||||
color: var(--td-text-color-secondary);
|
||||
background: color-mix(in srgb, var(--td-bg-color-container) 70%, transparent);
|
||||
border: 1px solid color-mix(in srgb, var(--td-component-stroke) 80%, transparent);
|
||||
}
|
||||
|
||||
// ── 双栏:面板等高,内容保持自然高度 ──
|
||||
.landing-body {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(300px, 380px);
|
||||
gap: 14px;
|
||||
align-items: stretch;
|
||||
|
||||
@media (max-width: 820px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.landing-main,
|
||||
.landing-aside {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// ── Group 面板(对齐 IM / SettingDrawer section)──
|
||||
.landing-group {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2px 16px 14px;
|
||||
border: 1px solid var(--td-component-stroke);
|
||||
border-radius: 10px;
|
||||
background: var(--td-bg-color-container);
|
||||
box-sizing: border-box;
|
||||
|
||||
.setting-drawer__section {
|
||||
padding: 12px 0 14px;
|
||||
border-bottom: 1px solid var(--td-component-stroke);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-drawer__section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--td-text-color-primary);
|
||||
margin: 0;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 3px;
|
||||
height: 14px;
|
||||
background: var(--td-brand-color);
|
||||
border-radius: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.section-head-extra {
|
||||
margin-left: auto;
|
||||
padding: 1px 7px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--td-text-color-disabled);
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
}
|
||||
|
||||
// ── 能力卡片 ──
|
||||
.capability-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-auto-rows: auto;
|
||||
gap: 10px;
|
||||
|
||||
@media (max-width: 520px) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
&--claw {
|
||||
.capability-card:last-child:nth-child(odd) {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.capability-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--td-component-stroke);
|
||||
border-radius: 8px;
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
box-sizing: border-box;
|
||||
|
||||
&__icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 7px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--td-bg-color-container);
|
||||
color: var(--td-brand-color);
|
||||
line-height: 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
:deep(.t-icon) {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: var(--td-text-color-primary);
|
||||
}
|
||||
|
||||
&__desc {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.landing-hint {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
border: 1px solid var(--td-component-stroke);
|
||||
|
||||
&__icon {
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
font-size: 14px;
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.landing-note {
|
||||
flex-shrink: 0;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.field-desc {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
|
||||
.credential-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.mono-text-input input) {
|
||||
font-family: var(--app-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.api-key-control.landing-api-control {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
|
||||
:deep(.t-input) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:deep(.t-button) {
|
||||
flex-shrink: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.code-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--td-component-stroke);
|
||||
overflow: hidden;
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
|
||||
&__code {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
font-family: var(--app-font-family-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
||||
font-size: 11px;
|
||||
line-height: 1.55;
|
||||
color: var(--td-text-color-primary);
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&__copy {
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
margin: 0 4px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.landing-steps {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.landing-step {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 9px 0;
|
||||
border-bottom: 1px solid var(--td-component-stroke);
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.landing-step-num {
|
||||
flex-shrink: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: color-mix(in srgb, var(--td-brand-color) 12%, transparent);
|
||||
color: var(--td-brand-color);
|
||||
}
|
||||
|
||||
.landing-step-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.landing-step-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: var(--td-text-color-primary);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.landing-step-desc {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
|
||||
.landing-step-action,
|
||||
.landing-step-embed {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.landing-step-embed.credential-row {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.landing-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.landing-footer-bar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--td-component-stroke);
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
box-sizing: border-box;
|
||||
|
||||
&__note {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
line-height: 1.55;
|
||||
color: var(--td-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.landing-meta {
|
||||
font-size: 11px;
|
||||
color: var(--td-text-color-placeholder);
|
||||
}
|
||||
Reference in New Issue
Block a user