feat(ui): add CodeBlock and CodeEditor components with Monaco and hljs

- add CodeBlock for read-only syntax-highlighted display (hljs, js/json)
- add CodeEditor wrapping Monaco editor with theme sync, focus state, and error state
- replace code textareas in snippet, credential, and step pages with CodeEditor
- replace code <pre> blocks in snippet and credential detail pages with CodeBlock
- make form cards full-width on pages with code editors
- add resize:vertical support to CodeEditor wrapper with automaticLayout
This commit is contained in:
2026-04-10 15:53:26 +03:00
parent 2046e64428
commit 1c13e068ad
21 changed files with 391 additions and 76 deletions
@@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { credentials } from '../../api';
import type { Credential } from '../../api';
import { Breadcrumbs, Button, Card, Input, Textarea } from '../../ui';
import { Breadcrumbs, Button, Card, Input, CodeEditor } from '../../ui';
import styles from '../Page.module.css';
export function EditCredentialPage() {
@@ -84,7 +84,7 @@ export function EditCredentialPage() {
{!loading && credential && (
<form onSubmit={handleSubmit} noValidate>
<Card className={styles.formCard}>
<Card className={styles.formCardFull}>
<div className={styles.formFields}>
<Input
label={t('credentials.form_name')}
@@ -98,17 +98,23 @@ export function EditCredentialPage() {
required
autoFocus
/>
<Textarea
label={t('credentials.form_data')}
placeholder={t('credentials.form_data_placeholder')}
value={data}
onChange={(e) => {
setData(e.target.value);
setDataError('');
}}
error={dataError || undefined}
rows={6}
/>
<div className={styles.formField}>
<label className={styles.fieldLabel}>
{t('credentials.form_data')}
<span className={styles.requiredMark}> *</span>
</label>
<CodeEditor
language="json"
value={data}
onChange={(v) => {
setData(v);
setDataError('');
}}
rows={6}
error={!!dataError}
/>
{dataError && <span className={styles.fieldError}>{dataError}</span>}
</div>
</div>
<div className={styles.formActions}>