refactor(snippets): adopt alias/title model and markdown UX

- rename snippet identity from name to alias and add required title fields

- migrate snippet API, forms, cards, and detail views to alias/title semantics

- render markdown descriptions with short card previews and split vendor chunks
This commit is contained in:
2026-04-10 23:26:36 +03:00
parent 11db983ea6
commit 7223371fae
21 changed files with 1821 additions and 85 deletions
+28 -3
View File
@@ -5,16 +5,29 @@ import { Settings, Pencil, Trash2, Plus, Download, Braces } from 'lucide-react';
import { parse as yamlParse } from 'yaml';
import { snippets } from '../../api';
import type { Snippet } from '../../api';
import { Breadcrumbs, Button, Card, ContextMenu, Timestamp, UuidBadge } from '../../ui';
import { Breadcrumbs, Button, Card, ContextMenu, MarkdownContent, Timestamp, UuidBadge } from '../../ui';
import styles from '../Page.module.css';
function firstParagraphBlocks(markdown: string, limit = 2): { preview: string; truncated: boolean } {
const blocks = markdown
.split(/\n\s*\n/g)
.map((block) => block.trim())
.filter(Boolean);
const sliced = blocks.slice(0, limit);
return {
preview: sliced.join('\n\n'),
truncated: blocks.length > limit,
};
}
function SnippetCard({ snippet, onDelete }: { snippet: Snippet; onDelete: (id: string) => void }) {
const { t } = useTranslation();
const navigate = useNavigate();
const header = (
<div className={styles.envCardHeader}>
<span className={styles.envCardName}>{snippet.name}</span>
<span className={styles.envCardName}>{snippet.title}</span>
<div onClick={(e) => e.stopPropagation()}>
<ContextMenu
align="right"
@@ -56,7 +69,19 @@ function SnippetCard({ snippet, onDelete }: { snippet: Snippet; onDelete: (id: s
footer={footer}
onClick={() => navigate(`/snippets/${snippet.id}`)}
>
{snippet.description && <p className={styles.muted}>{snippet.description}</p>}
<p className={styles.muted}>
{t('snippets.field_alias')}: <code className={styles.codeInline}>{snippet.alias}</code>
</p>
{snippet.description && (() => {
const { preview, truncated } = firstParagraphBlocks(snippet.description, 2);
if (!preview) return null;
return (
<>
<MarkdownContent content={preview} />
{truncated && <p className={styles.muted}>...</p>}
</>
);
})()}
</Card>
);
}