Enhance API documentation TOC with modern design and improved UX (#23490)

Co-authored-by: crazywoola <427733928@qq.com>
This commit is contained in:
lyzno1
2025-08-06 14:45:06 +08:00
committed by GitHub
parent 3344aaabb6
commit 1f15cba9a0
28 changed files with 462 additions and 109 deletions

View File

@@ -3,7 +3,7 @@
import { useEffect, useMemo, useState } from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { RiListUnordered } from '@remixicon/react'
import { RiCloseLine, RiListUnordered } from '@remixicon/react'
import TemplateEn from './template/template.en.mdx'
import TemplateZh from './template/template.zh.mdx'
import TemplateJa from './template/template.ja.mdx'
@@ -22,6 +22,7 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
const { t } = useTranslation()
const [toc, setToc] = useState<Array<{ href: string; text: string }>>([])
const [isTocExpanded, setIsTocExpanded] = useState(false)
const [activeSection, setActiveSection] = useState<string>('')
const { theme } = useTheme()
// Set initial TOC expanded state based on screen width
@@ -47,12 +48,47 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
return null
}).filter((item): item is { href: string; text: string } => item !== null)
setToc(tocItems)
// Set initial active section
if (tocItems.length > 0)
setActiveSection(tocItems[0].href.replace('#', ''))
}
}
setTimeout(extractTOC, 0)
}, [locale])
// Track scroll position for active section highlighting
useEffect(() => {
const handleScroll = () => {
const scrollContainer = document.querySelector('.scroll-container')
if (!scrollContainer || toc.length === 0)
return
// Find active section based on scroll position
let currentSection = ''
toc.forEach((item) => {
const targetId = item.href.replace('#', '')
const element = document.getElementById(targetId)
if (element) {
const rect = element.getBoundingClientRect()
// Consider section active if its top is above the middle of viewport
if (rect.top <= window.innerHeight / 2)
currentSection = targetId
}
})
if (currentSection && currentSection !== activeSection)
setActiveSection(currentSection)
}
const scrollContainer = document.querySelector('.scroll-container')
if (scrollContainer) {
scrollContainer.addEventListener('scroll', handleScroll)
handleScroll() // Initial check
return () => scrollContainer.removeEventListener('scroll', handleScroll)
}
}, [toc, activeSection])
// Handle TOC item click
const handleTocClick = (e: React.MouseEvent<HTMLAnchorElement>, item: { href: string; text: string }) => {
e.preventDefault()
@@ -84,40 +120,76 @@ const Doc = ({ apiBaseUrl }: DocProps) => {
return (
<div className="flex">
<div className={`fixed right-20 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
<div className={`fixed right-20 top-32 z-10 transition-all duration-150 ease-out ${isTocExpanded ? 'w-[280px]' : 'w-11'}`}>
{isTocExpanded
? (
<nav className="toc max-h-[calc(100vh-150px)] w-full overflow-y-auto rounded-lg border border-components-panel-border bg-components-panel-bg p-4 shadow-md">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3>
<nav className="toc flex max-h-[calc(100vh-150px)] w-full flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-background-default-hover shadow-xl">
<div className="relative z-10 flex items-center justify-between border-b border-components-panel-border-subtle bg-background-default-hover px-4 py-2.5">
<span className="text-xs font-medium uppercase tracking-wide text-text-tertiary">
{t('appApi.develop.toc')}
</span>
<button
onClick={() => setIsTocExpanded(false)}
className="text-text-tertiary hover:text-text-secondary"
className="group flex h-6 w-6 items-center justify-center rounded-md transition-colors hover:bg-state-base-hover"
aria-label="Close"
>
<RiCloseLine className="h-3 w-3 text-text-quaternary transition-colors group-hover:text-text-secondary" />
</button>
</div>
<ul className="space-y-2">
{toc.map((item, index) => (
<li key={index}>
<a
href={item.href}
className="text-text-secondary transition-colors duration-200 hover:text-text-primary hover:underline"
onClick={e => handleTocClick(e, item)}
>
{item.text}
</a>
</li>
))}
</ul>
<div className="from-components-panel-border-subtle/20 pointer-events-none absolute left-0 right-0 top-[41px] z-10 h-2 bg-gradient-to-b to-transparent"></div>
<div className="pointer-events-none absolute left-0 right-0 top-[43px] z-10 h-3 bg-gradient-to-b from-background-default-hover to-transparent"></div>
<div className="relative flex-1 overflow-y-auto px-3 py-3 pt-1">
{toc.length === 0 ? (
<div className="px-2 py-8 text-center text-xs text-text-quaternary">
{t('appApi.develop.noContent')}
</div>
) : (
<ul className="space-y-0.5">
{toc.map((item, index) => {
const isActive = activeSection === item.href.replace('#', '')
return (
<li key={index}>
<a
href={item.href}
onClick={e => handleTocClick(e, item)}
className={cn(
'group relative flex items-center rounded-md px-3 py-2 text-[13px] transition-all duration-200',
isActive
? 'bg-state-base-hover font-medium text-text-primary'
: 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
)}
>
<span
className={cn(
'mr-2 h-1.5 w-1.5 rounded-full transition-all duration-200',
isActive
? 'scale-100 bg-text-accent'
: 'scale-75 bg-components-panel-border',
)}
/>
<span className="flex-1 truncate">
{item.text}
</span>
</a>
</li>
)
})}
</ul>
)}
</div>
<div className="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-4 rounded-b-xl bg-gradient-to-t from-background-default-hover to-transparent"></div>
</nav>
)
: (
<button
onClick={() => setIsTocExpanded(true)}
className="flex h-10 w-10 items-center justify-center rounded-full border border-components-panel-border bg-components-button-secondary-bg shadow-md transition-colors duration-200 hover:bg-components-button-secondary-bg-hover"
className="group flex h-11 w-11 items-center justify-center rounded-full border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg transition-all duration-150 hover:bg-background-default-hover hover:shadow-xl"
aria-label="Open table of contents"
>
<RiListUnordered className="h-6 w-6 text-components-button-secondary-text" />
<RiListUnordered className="h-5 w-5 text-text-tertiary transition-colors group-hover:text-text-secondary" />
</button>
)}
</div>

View File

@@ -1,8 +1,8 @@
'use client'
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { RiListUnordered } from '@remixicon/react'
import { RiCloseLine, RiListUnordered } from '@remixicon/react'
import TemplateEn from './template/template.en.mdx'
import TemplateZh from './template/template.zh.mdx'
import TemplateJa from './template/template.ja.mdx'
@@ -30,6 +30,7 @@ const Doc = ({ appDetail }: IDocProps) => {
const { t } = useTranslation()
const [toc, setToc] = useState<Array<{ href: string; text: string }>>([])
const [isTocExpanded, setIsTocExpanded] = useState(false)
const [activeSection, setActiveSection] = useState<string>('')
const { theme } = useTheme()
const variables = appDetail?.model_config?.configs?.prompt_variables || []
@@ -59,13 +60,43 @@ const Doc = ({ appDetail }: IDocProps) => {
return null
}).filter((item): item is { href: string; text: string } => item !== null)
setToc(tocItems)
if (tocItems.length > 0)
setActiveSection(tocItems[0].href.replace('#', ''))
}
}
// Run after component has rendered
setTimeout(extractTOC, 0)
}, [appDetail, locale])
useEffect(() => {
const handleScroll = () => {
const scrollContainer = document.querySelector('.overflow-auto')
if (!scrollContainer || toc.length === 0)
return
let currentSection = ''
toc.forEach((item) => {
const targetId = item.href.replace('#', '')
const element = document.getElementById(targetId)
if (element) {
const rect = element.getBoundingClientRect()
if (rect.top <= window.innerHeight / 2)
currentSection = targetId
}
})
if (currentSection && currentSection !== activeSection)
setActiveSection(currentSection)
}
const scrollContainer = document.querySelector('.overflow-auto')
if (scrollContainer) {
scrollContainer.addEventListener('scroll', handleScroll)
handleScroll()
return () => scrollContainer.removeEventListener('scroll', handleScroll)
}
}, [toc, activeSection])
const handleTocClick = (e: React.MouseEvent<HTMLAnchorElement>, item: { href: string; text: string }) => {
e.preventDefault()
const targetId = item.href.replace('#', '')
@@ -82,94 +113,128 @@ const Doc = ({ appDetail }: IDocProps) => {
}
}
}
const Template = useMemo(() => {
if (appDetail?.mode === 'chat' || appDetail?.mode === 'agent-chat') {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
}
if (appDetail?.mode === 'advanced-chat') {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateAdvancedChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateAdvancedChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateAdvancedChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
}
if (appDetail?.mode === 'workflow') {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateWorkflowZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateWorkflowJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateWorkflowEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
}
if (appDetail?.mode === 'completion') {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
}
return null
}, [appDetail, locale, variables, inputs])
return (
<div className="flex">
<div className={`fixed right-8 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
<div className={`fixed right-20 top-32 z-10 transition-all duration-150 ease-out ${isTocExpanded ? 'w-[280px]' : 'w-11'}`}>
{isTocExpanded
? (
<nav className="toc max-h-[calc(100vh-150px)] w-full overflow-y-auto rounded-lg border border-components-panel-border bg-components-panel-bg p-4 shadow-md">
<div className="mb-4 flex items-center justify-between">
<h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3>
<nav className="toc flex max-h-[calc(100vh-150px)] w-full flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-background-default-hover shadow-xl">
<div className="relative z-10 flex items-center justify-between border-b border-components-panel-border-subtle bg-background-default-hover px-4 py-2.5">
<span className="text-xs font-medium uppercase tracking-wide text-text-tertiary">
{t('appApi.develop.toc')}
</span>
<button
onClick={() => setIsTocExpanded(false)}
className="text-text-tertiary hover:text-text-secondary"
className="group flex h-6 w-6 items-center justify-center rounded-md transition-colors hover:bg-state-base-hover"
aria-label="Close"
>
<RiCloseLine className="h-3 w-3 text-text-quaternary transition-colors group-hover:text-text-secondary" />
</button>
</div>
<ul className="space-y-2">
{toc.map((item, index) => (
<li key={index}>
<a
href={item.href}
className="text-text-secondary transition-colors duration-200 hover:text-text-primary hover:underline"
onClick={e => handleTocClick(e, item)}
>
{item.text}
</a>
</li>
))}
</ul>
<div className="from-components-panel-border-subtle/20 pointer-events-none absolute left-0 right-0 top-[41px] z-10 h-2 bg-gradient-to-b to-transparent"></div>
<div className="pointer-events-none absolute left-0 right-0 top-[43px] z-10 h-3 bg-gradient-to-b from-background-default-hover to-transparent"></div>
<div className="relative flex-1 overflow-y-auto px-3 py-3 pt-1">
{toc.length === 0 ? (
<div className="px-2 py-8 text-center text-xs text-text-quaternary">
{t('appApi.develop.noContent')}
</div>
) : (
<ul className="space-y-0.5">
{toc.map((item, index) => {
const isActive = activeSection === item.href.replace('#', '')
return (
<li key={index}>
<a
href={item.href}
onClick={e => handleTocClick(e, item)}
className={cn(
'group relative flex items-center rounded-md px-3 py-2 text-[13px] transition-all duration-200',
isActive
? 'bg-state-base-hover font-medium text-text-primary'
: 'text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary',
)}
>
<span
className={cn(
'mr-2 h-1.5 w-1.5 rounded-full transition-all duration-200',
isActive
? 'scale-100 bg-text-accent'
: 'scale-75 bg-components-panel-border',
)}
/>
<span className="flex-1 truncate">
{item.text}
</span>
</a>
</li>
)
})}
</ul>
)}
</div>
<div className="pointer-events-none absolute bottom-0 left-0 right-0 z-10 h-4 rounded-b-xl bg-gradient-to-t from-background-default-hover to-transparent"></div>
</nav>
)
: (
<button
onClick={() => setIsTocExpanded(true)}
className="flex h-10 w-10 items-center justify-center rounded-full border border-components-panel-border bg-components-button-secondary-bg shadow-md transition-colors duration-200 hover:bg-components-button-secondary-bg-hover"
className="group flex h-11 w-11 items-center justify-center rounded-full border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg transition-all duration-150 hover:bg-background-default-hover hover:shadow-xl"
aria-label="Open table of contents"
>
<RiListUnordered className="h-6 w-6 text-components-button-secondary-text" />
<RiListUnordered className="h-5 w-5 text-text-tertiary transition-colors group-hover:text-text-secondary" />
</button>
)}
</div>
<article className={cn('prose-xl prose', theme === Theme.dark && 'prose-invert')} >
{(appDetail?.mode === 'chat' || appDetail?.mode === 'agent-chat') && (
(() => {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
})()
)}
{appDetail?.mode === 'advanced-chat' && (
(() => {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateAdvancedChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateAdvancedChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateAdvancedChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
})()
)}
{appDetail?.mode === 'workflow' && (
(() => {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateWorkflowZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateWorkflowJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateWorkflowEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
})()
)}
{appDetail?.mode === 'completion' && (
(() => {
switch (locale) {
case LanguagesSupported[1]:
return <TemplateZh appDetail={appDetail} variables={variables} inputs={inputs} />
case LanguagesSupported[7]:
return <TemplateJa appDetail={appDetail} variables={variables} inputs={inputs} />
default:
return <TemplateEn appDetail={appDetail} variables={variables} inputs={inputs} />
}
})()
)}
<article className={cn('prose-xl prose', theme === Theme.dark && 'prose-invert')}>
{Template}
</article>
</div>
)

View File

@@ -448,7 +448,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
url='/text-to-audio'
method='POST'
title='テキストから音声'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -423,7 +423,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
url='/text-to-audio'
method='POST'
title='文字转语音'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -1136,7 +1136,7 @@ Chat applications support session persistence, allowing previous chat history to
url='/audio-to-text'
method='POST'
title='Speech to Text'
name='#audio'
name='#audio-to-text'
/>
<Row>
<Col>
@@ -1187,7 +1187,7 @@ Chat applications support session persistence, allowing previous chat history to
url='/text-to-audio'
method='POST'
title='Text to Audio'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -1136,7 +1136,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
url='/audio-to-text'
method='POST'
title='音声からテキストへ'
name='#audio'
name='#audio-to-text'
/>
<Row>
<Col>
@@ -1187,7 +1187,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
url='/text-to-audio'
method='POST'
title='テキストから音声へ'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -1174,7 +1174,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
url='/audio-to-text'
method='POST'
title='语音转文字'
name='#audio'
name='#audio-to-text'
/>
<Row>
<Col>
@@ -1222,7 +1222,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
url='/text-to-audio'
method='POST'
title='文字转语音'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -1170,7 +1170,7 @@ Chat applications support session persistence, allowing previous chat history to
url='/audio-to-text'
method='POST'
title='Speech to Text'
name='#audio'
name='#audio-to-text'
/>
<Row>
<Col>
@@ -1221,7 +1221,7 @@ Chat applications support session persistence, allowing previous chat history to
url='/text-to-audio'
method='POST'
title='Text to Audio'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -1169,7 +1169,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
url='/audio-to-text'
method='POST'
title='音声からテキストへ'
name='#audio'
name='#audio-to-text'
/>
<Row>
<Col>
@@ -1220,7 +1220,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
url='/text-to-audio'
method='POST'
title='テキストから音声へ'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -1185,7 +1185,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
url='/audio-to-text'
method='POST'
title='语音转文字'
name='#audio'
name='#audio-to-text'
/>
<Row>
<Col>
@@ -1233,7 +1233,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
url='/text-to-audio'
method='POST'
title='文字转语音'
name='#audio'
name='#text-to-audio'
/>
<Row>
<Col>

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Auf 50% vergrößern',
zoomTo100: 'Auf 100% vergrößern',
zoomToFit: 'An Bildschirm anpassen',
selectionAlignment: 'Ausrichtung der Auswahl',
alignLeft: 'Links',
alignTop: 'Nach oben',
distributeVertical: 'Vertikaler Raum',
alignBottom: 'Unteres',
distributeHorizontal: 'Horizontaler Raum',
vertical: 'Senkrecht',
alignMiddle: 'Mitte',
alignCenter: 'Mitte',
alignRight: 'Rechts',
alignNodes: 'Knoten ausrichten',
horizontal: 'Horizontal',
},
panel: {
userInputField: 'Benutzereingabefeld',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Zoom al 50%',
zoomTo100: 'Zoom al 100%',
zoomToFit: 'Ajustar al tamaño',
alignTop: 'Arriba',
alignBottom: 'Fondo',
alignNodes: 'Alinear nodos',
alignCenter: 'Centro',
selectionAlignment: 'Alineación de selección',
horizontal: 'Horizontal',
distributeHorizontal: 'Espaciar horizontalmente',
vertical: 'Vertical',
distributeVertical: 'Espaciar verticalmente',
alignMiddle: 'medio',
alignLeft: 'izquierdo',
alignRight: 'derecho',
},
panel: {
userInputField: 'Campo de entrada del usuario',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'بزرگ‌نمایی به 50%',
zoomTo100: 'بزرگ‌نمایی به 100%',
zoomToFit: 'تناسب با اندازه',
horizontal: 'افقی',
alignBottom: 'پایین',
alignRight: 'راست',
vertical: 'عمودی',
alignCenter: 'مرکز',
alignLeft: 'چپ',
distributeVertical: 'فضا عمودی',
distributeHorizontal: 'فضا به صورت افقی',
alignTop: 'بالا',
alignNodes: 'تراز کردن گره ها',
selectionAlignment: 'تراز انتخاب',
alignMiddle: 'میانه',
},
panel: {
userInputField: 'فیلد ورودی کاربر',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Zoomer à 50%',
zoomTo100: 'Zoomer à 100%',
zoomToFit: 'Zoomer pour ajuster',
alignBottom: 'Fond',
alignLeft: 'Gauche',
alignCenter: 'Centre',
alignTop: 'Retour au début',
alignNodes: 'Aligner les nœuds',
distributeHorizontal: 'Espace horizontal',
alignMiddle: 'Milieu',
horizontal: 'Horizontal',
selectionAlignment: 'Alignement de la sélection',
alignRight: 'Droite',
vertical: 'Vertical',
distributeVertical: 'Espace vertical',
},
panel: {
userInputField: 'Champ de saisie de l\'utilisateur',

View File

@@ -298,6 +298,18 @@ const translation = {
zoomTo50: '50% पर ज़ूम करें',
zoomTo100: '100% पर ज़ूम करें',
zoomToFit: 'फिट करने के लिए ज़ूम करें',
alignRight: 'सही',
alignLeft: 'बाईं ओर',
alignTop: 'शीर्ष',
horizontal: 'क्षैतिज',
alignNodes: 'नोड्स को संरेखित करें',
selectionAlignment: 'चयन संरेखण',
alignCenter: 'केंद्र',
vertical: 'ऊर्ध्वाधर',
distributeHorizontal: 'क्षैतिज स्पेस',
alignBottom: 'तल',
distributeVertical: 'अंतरिक्ष को वर्टिकल रूप से',
alignMiddle: 'मध्य',
},
panel: {
userInputField: 'उपयोगकर्ता इनपुट फ़ील्ड',

View File

@@ -301,6 +301,18 @@ const translation = {
zoomTo50: 'Zoom al 50%',
zoomTo100: 'Zoom al 100%',
zoomToFit: 'Zoom per Adattare',
alignRight: 'A destra',
selectionAlignment: 'Allineamento della selezione',
alignBottom: 'Fondoschiena',
alignTop: 'In alto',
vertical: 'Verticale',
alignCenter: 'Centro',
alignLeft: 'A sinistra',
alignMiddle: 'Mezzo',
horizontal: 'Orizzontale',
alignNodes: 'Allinea nodi',
distributeHorizontal: 'Spazia orizzontalmente',
distributeVertical: 'Spazia verticalmente',
},
panel: {
userInputField: 'Campo di Input Utente',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: '50% サイズ',
zoomTo100: '等倍表示',
zoomToFit: '画面に合わせる',
horizontal: '横',
alignBottom: '底',
alignNodes: 'ノードを整列させる',
vertical: '垂直',
alignLeft: '左',
alignTop: 'トップ',
alignRight: '右',
alignMiddle: '中間',
distributeVertical: '垂直にスペースを',
alignCenter: 'センター',
selectionAlignment: '選択の整列',
distributeHorizontal: '空間を水平方向に',
},
variableReference: {
noAvailableVars: '利用可能な変数がありません',

View File

@@ -308,6 +308,18 @@ const translation = {
zoomTo50: '50% 로 확대',
zoomTo100: '100% 로 확대',
zoomToFit: '화면에 맞게 확대',
alignCenter: '중',
alignRight: '오른쪽',
alignLeft: '왼쪽',
vertical: '세로',
alignTop: '맨 위로',
alignMiddle: '중간',
alignNodes: '노드 정렬',
distributeVertical: '수직 공간',
horizontal: '가로',
selectionAlignment: '선택 정렬',
alignBottom: '밑바닥',
distributeHorizontal: '수평 공간',
},
panel: {
userInputField: '사용자 입력 필드',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Powiększ do 50%',
zoomTo100: 'Powiększ do 100%',
zoomToFit: 'Dopasuj do ekranu',
alignMiddle: 'Środek',
alignTop: 'Do góry',
distributeHorizontal: 'Odstęp w poziomie',
alignCenter: 'Centrum',
alignRight: 'Prawy',
alignNodes: 'Wyrównywanie węzłów',
selectionAlignment: 'Wyrównanie zaznaczenia',
horizontal: 'Poziomy',
distributeVertical: 'Przestrzeń w pionie',
alignBottom: 'Dno',
alignLeft: 'Lewy',
vertical: 'Pionowy',
},
panel: {
userInputField: 'Pole wprowadzania użytkownika',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Aproximar para 50%',
zoomTo100: 'Aproximar para 100%',
zoomToFit: 'Aproximar para ajustar',
vertical: 'Vertical',
alignNodes: 'Alinhar nós',
selectionAlignment: 'Alinhamento de seleção',
alignLeft: 'Esquerda',
alignBottom: 'Fundo',
distributeHorizontal: 'Espaço horizontalmente',
alignMiddle: 'Meio',
alignRight: 'Certo',
horizontal: 'Horizontal',
distributeVertical: 'Espaço Verticalmente',
alignCenter: 'Centro',
alignTop: 'Início',
},
panel: {
userInputField: 'Campo de entrada do usuário',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Mărește la 50%',
zoomTo100: 'Mărește la 100%',
zoomToFit: 'Mărește pentru a se potrivi',
horizontal: 'Orizontal',
selectionAlignment: 'Alinierea selecției',
vertical: 'Vertical',
alignRight: 'Dreapta',
alignLeft: 'Stânga',
alignMiddle: 'Mijloc',
distributeVertical: 'Spațiu vertical',
alignCenter: 'Centru',
distributeHorizontal: 'Spațiu orizontal',
alignBottom: 'Fund',
alignTop: 'Culme',
alignNodes: 'Alinierea nodurilor',
},
panel: {
userInputField: 'Câmp de introducere utilizator',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Масштаб 50%',
zoomTo100: 'Масштаб 100%',
zoomToFit: 'По размеру',
alignTop: 'Вверх',
alignBottom: 'Дно',
alignRight: 'Правильно',
distributeHorizontal: 'Пространство по горизонтали',
alignMiddle: 'Середина',
vertical: 'Вертикальный',
alignCenter: 'Центр',
alignLeft: 'Налево',
selectionAlignment: 'Выравнивание выделения',
horizontal: 'Горизонтальный',
alignNodes: 'Выравнивание узлов',
distributeVertical: 'Пространство по вертикали',
},
panel: {
userInputField: 'Поле ввода пользователя',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomIn: 'Zoom in',
zoomTo50: 'Povečaj na 50%',
zoomTo100: 'Povečaj na 100%',
alignMiddle: 'Srednji',
alignBottom: 'Dno',
alignCenter: 'Center',
distributeVertical: 'Razmik navpično',
alignRight: 'Desno',
alignTop: 'Vrh',
vertical: 'Navpičen',
distributeHorizontal: 'Razmik vodoravno',
selectionAlignment: 'Poravnava izbora',
alignNodes: 'Poravnava vozlišč',
horizontal: 'Vodoraven',
alignLeft: 'Levo',
},
variableReference: {
conversationVars: 'pogovorne spremenljivke',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'ซูมไปที่ 50%',
zoomTo100: 'ซูมไปที่ 100%',
zoomToFit: 'ซูมให้พอดี',
alignBottom: 'ก้น',
alignCenter: 'ศูนย์กลาง',
alignMiddle: 'กลาง',
horizontal: 'แนวราบ',
vertical: 'ซึ่งตั้งตรง',
alignTop: 'ด้านบน',
distributeVertical: 'พื้นที่ในแนวตั้ง',
alignLeft: 'ซ้าย',
selectionAlignment: 'การจัดตําแหน่งการเลือก',
distributeHorizontal: 'ช่องว่างในแนวนอน',
alignRight: 'ขวา',
alignNodes: 'จัดตําแหน่งโหนด',
},
panel: {
userInputField: 'ฟิลด์ป้อนข้อมูลของผู้ใช้',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: '%50 Yakınlaştır',
zoomTo100: '%100 Yakınlaştır',
zoomToFit: 'Sığdıracak Şekilde Yakınlaştır',
alignCenter: 'Orta',
alignMiddle: 'Orta',
alignLeft: 'Sol',
alignNodes: 'Düğümleri Hizala',
vertical: 'Dikey',
alignRight: 'Sağ',
alignTop: 'Sayfanın Üstü',
alignBottom: 'Dip',
selectionAlignment: 'Seçim Hizalama',
distributeHorizontal: 'Yatay Boşluk',
horizontal: 'Yatay',
distributeVertical: 'Dikey Boşluk',
},
panel: {
userInputField: 'Kullanıcı Giriş Alanı',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Збільшити до 50%',
zoomTo100: 'Збільшити до 100%',
zoomToFit: 'Збільшити для підгонки',
alignCenter: 'Центр',
alignRight: 'Праворуч',
vertical: 'Вертикальні',
alignBottom: 'Низ',
alignLeft: 'Ліворуч',
alignTop: 'Верх',
horizontal: 'Горизонтальні',
alignMiddle: 'Середній',
distributeVertical: 'Простір по вертикалі',
distributeHorizontal: 'Простір по горизонталі',
selectionAlignment: 'Вирівнювання вибору',
alignNodes: 'Вирівнювання вузлів',
},
panel: {
userInputField: 'Поле введення користувача',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: 'Phóng to 50%',
zoomTo100: 'Phóng to 100%',
zoomToFit: 'Phóng to vừa màn hình',
alignBottom: 'Đáy',
alignMiddle: 'Trung',
alignRight: 'Bên phải',
alignNodes: 'Căn chỉnh các nút',
alignLeft: 'Bên trái',
horizontal: 'Ngang',
alignCenter: 'Trung tâm',
alignTop: 'Đỉnh',
distributeVertical: 'Không gian theo chiều dọc',
selectionAlignment: 'Căn chỉnh lựa chọn',
distributeHorizontal: 'Không gian theo chiều ngang',
vertical: 'Thẳng đứng',
},
panel: {
userInputField: 'Trường đầu vào của người dùng',

View File

@@ -287,6 +287,18 @@ const translation = {
zoomTo50: '縮放到 50%',
zoomTo100: '放大到 100%',
zoomToFit: '自適應視圖',
alignNodes: '對齊節點Align Nodes',
distributeVertical: '垂直空間',
alignLeft: '左',
distributeHorizontal: '水平空間',
vertical: '垂直',
alignTop: '返回頁首',
alignCenter: '中心',
horizontal: '水準',
selectionAlignment: '選擇對齊',
alignRight: '右',
alignBottom: '底',
alignMiddle: '中間',
},
panel: {
userInputField: '用戶輸入字段',