From 3472cdf0d7d2758a7d4ad298ae1dc139fd16cfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=BCrgin-Fix?= Date: Wed, 29 Jul 2026 03:59:21 +0200 Subject: [PATCH 1/4] Release v1.6.0: CodeEditor + FullscreenPanel promotions, PageHeading actions slot, Card sm size Additive package changes for the app-wide frontend consistency refactor: - Promote CodeEditor (replacing the app's JsonEditor/MarkdownEditor) and FullscreenPanel into the kit, both lazy-loading CodeMirror consistently with CodePreview's existing convention. - Add an optional #actions slot to PageHeading and a purple legacy tone alias to StatusBadge, closing gaps the app was working around locally. - Add a Card size="sm" variant for nested/compact container use. Co-Authored-By: Claude Sonnet 5 --- package-lock.json | 21 +++ package.json | 10 +- src/components/atoms/StatusBadge.stories.ts | 6 + src/components/molecules/Card.stories.ts | 9 + src/components/molecules/Card.vue | 10 +- .../molecules/FullscreenPanel.stories.ts | 27 +++ src/components/molecules/FullscreenPanel.vue | 38 +++++ .../molecules/PageHeading.stories.ts | 17 ++ src/components/molecules/PageHeading.vue | 49 +++--- .../organisms/CodeEditor.stories.ts | 40 +++++ src/components/organisms/CodeEditor.vue | 155 ++++++++++++++++++ src/components/organisms/CodePreview.vue | 22 +-- src/composables/useEscapeKey.ts | 14 +- src/helpers/codeMirrorTheme.ts | 48 ++++++ src/helpers/tone.ts | 6 +- src/index.ts | 6 + 16 files changed, 437 insertions(+), 41 deletions(-) create mode 100644 src/components/molecules/FullscreenPanel.stories.ts create mode 100644 src/components/molecules/FullscreenPanel.vue create mode 100644 src/components/organisms/CodeEditor.stories.ts create mode 100644 src/components/organisms/CodeEditor.vue create mode 100644 src/helpers/codeMirrorTheme.ts diff --git a/package-lock.json b/package-lock.json index 75dabff..41d20b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,10 @@ "tailwind-merge": "^3.0.0" }, "devDependencies": { + "@codemirror/commands": "^6.10.4", "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/language": "^6.12.4", "@codemirror/state": "^6.7.0", "@codemirror/view": "^6.43.4", "@playwright/test": "^1.48.0", @@ -40,12 +42,18 @@ "vue": "^3.5.0" }, "peerDependenciesMeta": { + "@codemirror/commands": { + "optional": true + }, "@codemirror/lang-json": { "optional": true }, "@codemirror/lang-markdown": { "optional": true }, + "@codemirror/language": { + "optional": true + }, "@codemirror/state": { "optional": true }, @@ -154,6 +162,19 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@codemirror/commands": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz", + "integrity": "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.7.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, "node_modules/@codemirror/lang-css": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", diff --git a/package.json b/package.json index eb88829..b17a482 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codebar-ag/storybook", - "version": "1.5.0", + "version": "1.6.0", "description": "codebar-ag DocuHub — shared Vue 3 + Tailwind v4 design-system atoms and tokens, documented in Storybook.", "license": "MIT", "author": "codebar Solutions AG", @@ -54,11 +54,19 @@ }, "@codemirror/lang-markdown": { "optional": true + }, + "@codemirror/commands": { + "optional": true + }, + "@codemirror/language": { + "optional": true } }, "devDependencies": { + "@codemirror/commands": "^6.10.4", "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/language": "^6.12.4", "@codemirror/state": "^6.7.0", "@codemirror/view": "^6.43.4", "@playwright/test": "^1.48.0", diff --git a/src/components/atoms/StatusBadge.stories.ts b/src/components/atoms/StatusBadge.stories.ts index cbfd28b..264faf8 100644 --- a/src/components/atoms/StatusBadge.stories.ts +++ b/src/components/atoms/StatusBadge.stories.ts @@ -37,3 +37,9 @@ export const InstanceStatuses: Story = { `, }), }; + +// `purple` is a deprecated legacy alias (rides `info`) for non-severity +// statuses like "seen this before" — e.g. a duplicate webhook delivery. +export const LegacyPurpleAlias: Story = { + args: { variant: 'purple', label: 'Duplicate', dot: false }, +}; diff --git a/src/components/molecules/Card.stories.ts b/src/components/molecules/Card.stories.ts index 3daf674..47de170 100644 --- a/src/components/molecules/Card.stories.ts +++ b/src/components/molecules/Card.stories.ts @@ -30,3 +30,12 @@ export const WithActionsAndFooter: Story = { `, }), }; + +export const Small: Story = { + render: () => ({ + components: { Card }, + template: ` +

A denser card for rows nested inside a larger container.

+
`, + }), +}; diff --git a/src/components/molecules/Card.vue b/src/components/molecules/Card.vue index b800a0a..cae8127 100644 --- a/src/components/molecules/Card.vue +++ b/src/components/molecules/Card.vue @@ -13,8 +13,10 @@ const props = withDefaults( variant?: 'default' | 'danger'; hoverable?: boolean; // 'lg' is for screens with a single, centered card as the focal point - // (e.g. auth screens) where the default density reads cramped. - size?: 'md' | 'lg'; + // (e.g. auth screens) where the default density reads cramped. 'sm' + // is for cards nested inside another card/list — a denser container, + // not a different component. + size?: 'sm' | 'md' | 'lg'; }>(), { title: null, description: null, padded: true, variant: 'default', hoverable: false, size: 'md' }, ); @@ -25,18 +27,22 @@ const { rootAttrs, classAttr } = useRootAttrs(); const variants: Record = { default: '', danger: 'border-danger-line/60' }; const headerPadding: Record = { + sm: 'px-3 sm:px-4 pt-3 pb-2.5', md: 'px-4 sm:px-5 pt-5 pb-4', lg: 'px-5 sm:px-6 pt-6 pb-5', }; const bodyPadding: Record = { + sm: 'px-3 sm:px-4 py-3', md: 'px-4 sm:px-5 py-5', lg: 'px-5 sm:px-6 py-6', }; const footerPadding: Record = { + sm: 'px-3 sm:px-4 py-2.5', md: 'px-4 sm:px-5 py-4', lg: 'px-5 sm:px-6 py-5', }; const titleSize: Record = { + sm: 'text-sm', md: 'text-lg', lg: 'text-xl', }; diff --git a/src/components/molecules/FullscreenPanel.stories.ts b/src/components/molecules/FullscreenPanel.stories.ts new file mode 100644 index 0000000..fb2fe8e --- /dev/null +++ b/src/components/molecules/FullscreenPanel.stories.ts @@ -0,0 +1,27 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite'; +import FullscreenPanel from './FullscreenPanel.vue'; +import Button from '../atoms/Button.vue'; + +const meta: Meta = { + title: 'Molecules/FullscreenPanel', + component: FullscreenPanel, + parameters: { layout: 'padded' }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ({ + components: { FullscreenPanel, Button }, + template: ` +
+ {{ isFullscreen ? 'Fullscreen' : 'Inline' }} + +
+

Editor content lives in the default slot.

+
`, + }), +}; diff --git a/src/components/molecules/FullscreenPanel.vue b/src/components/molecules/FullscreenPanel.vue new file mode 100644 index 0000000..11fe9d7 --- /dev/null +++ b/src/components/molecules/FullscreenPanel.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/components/molecules/PageHeading.stories.ts b/src/components/molecules/PageHeading.stories.ts index 49a8f82..3b9358b 100644 --- a/src/components/molecules/PageHeading.stories.ts +++ b/src/components/molecules/PageHeading.stories.ts @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite'; import PageHeading from './PageHeading.vue'; +import Button from '../atoms/Button.vue'; const meta: Meta = { title: 'Molecules/PageHeading', @@ -30,3 +31,19 @@ export const WithDescription: Story = { `, }), }; + +export const WithActions: Story = { + render: () => ({ + components: { PageHeading, Button }, + template: ` + File cabinets + + + `, + }), +}; diff --git a/src/components/molecules/PageHeading.vue b/src/components/molecules/PageHeading.vue index 84e0da7..fc49fca 100644 --- a/src/components/molecules/PageHeading.vue +++ b/src/components/molecules/PageHeading.vue @@ -1,8 +1,9 @@ diff --git a/src/components/organisms/CodeEditor.stories.ts b/src/components/organisms/CodeEditor.stories.ts new file mode 100644 index 0000000..968cb02 --- /dev/null +++ b/src/components/organisms/CodeEditor.stories.ts @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/vue3-vite'; +import CodeEditor from './CodeEditor.vue'; + +const meta: Meta = { + title: 'Organisms/CodeEditor', + component: CodeEditor, + parameters: { layout: 'padded' }, + args: { + modelValue: '{\n "vendor": "string",\n "invoice_number": "string"\n}', + language: 'json', + }, + render: (args) => ({ + components: { CodeEditor }, + setup: () => ({ args }), + template: '
', + }), +}; + +export default meta; +type Story = StoryObj; + +export const Json: Story = {}; + +export const Markdown: Story = { + args: { + modelValue: '# Extraction prompt\n\nSummarize the invoice fields below.', + language: 'markdown', + }, +}; + +export const ReadOnlyEmpty: Story = { + render: () => ({ + components: { CodeEditor }, + template: '
', + }), +}; + +export const AutoHeight: Story = { + args: { autoHeight: true, maxHeight: '12rem' }, +}; diff --git a/src/components/organisms/CodeEditor.vue b/src/components/organisms/CodeEditor.vue new file mode 100644 index 0000000..7a8fd4e --- /dev/null +++ b/src/components/organisms/CodeEditor.vue @@ -0,0 +1,155 @@ + + + diff --git a/src/components/organisms/CodePreview.vue b/src/components/organisms/CodePreview.vue index 19db003..3506d03 100644 --- a/src/components/organisms/CodePreview.vue +++ b/src/components/organisms/CodePreview.vue @@ -1,6 +1,7 @@