xertica-ui 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [2.1.2] — 2026-05-14
11
+
12
+ ### Fixed
13
+
14
+ - **`MarkdownMessage` — suporte a tabelas GFM** — o parser regex do componente não convertia blocos de tabela Markdown (`| col | col |`) em HTML. Adicionada transformação de tabelas GFM completa (header, separador, linhas de dados) com estilos do design system (`border-border`, `hover:bg-muted/50`, `rounded-[var(--radius)]`, `overflow-x-auto`). A transformação roda antes das substituições de quebra de linha para evitar `<br/>` dentro das células.
15
+
16
+ ### Added
17
+
18
+ - **Template — `AssistantPage`** — nova página de assistente completa: sidebar em modo `assistant` aberta por padrão (largura 320px), botão "Nova Conversa" (`variant="secondary"`), busca de conversas com filtro em tempo real, histórico de 8 conversas de exemplo em 3 grupos (Hoje / Ontem / Esta semana), modal de exclusão com `AlertDialog`, modal de renomeação com `Dialog` + `Input`, simulação de conteúdo ao selecionar conversa via `initialMessages`, botão "Voltar" em `variant` primário no `Header`.
19
+ - **Template — `AppLayout`** — novas props `sidebarVariant?: 'default' | 'assistant'` e `sidebarProps?: Record<string, any>` para permitir que páginas customizem a sidebar sem quebrar o contrato do layout.
20
+
21
+ ---
22
+
10
23
  ## [2.1.1] — 2026-05-14
11
24
 
12
25
  ### Fixed
package/bin/cli.ts CHANGED
@@ -18,7 +18,7 @@ const program = new Command();
18
18
  program
19
19
  .name('xertica-ui')
20
20
  .description('CLI to initialize Xertica UI projects')
21
- .version('2.0.6');
21
+ .version('2.1.2');
22
22
 
23
23
  program
24
24
  .command('init')
@@ -64,8 +64,34 @@ export function MarkdownMessage({ content, className = '' }: MarkdownMessageProp
64
64
  return `<ol class="list-decimal my-2">${items}</ol>`;
65
65
  });
66
66
 
67
+ // GFM Tables — must run before line-break transforms
68
+ html = html.replace(/((?:^\|.+\|[ \t]*(?:\r?\n|$))+)/gm, (match) => {
69
+ const rows = match.trim().split(/\r?\n/).filter(line => line.trim());
70
+ if (rows.length < 2) return match;
71
+
72
+ const isSeparatorRow = /^\|[\s\-:|]+\|$/.test(rows[1].trim());
73
+ if (!isSeparatorRow) return match;
74
+
75
+ const parseCells = (row: string) =>
76
+ row.split('|').slice(1, -1).map(cell => cell.trim());
77
+
78
+ const headerCells = parseCells(rows[0])
79
+ .map(cell => `<th class="px-3 py-2 text-left font-medium text-xs uppercase tracking-wide border-b border-border">${cell}</th>`)
80
+ .join('');
81
+
82
+ const bodyRows = rows.slice(2)
83
+ .map(row =>
84
+ `<tr class="border-b border-border last:border-0 hover:bg-muted/50">${
85
+ parseCells(row).map(cell => `<td class="px-3 py-2 text-sm">${cell}</td>`).join('')
86
+ }</tr>`
87
+ )
88
+ .join('');
89
+
90
+ return `<div class="overflow-x-auto my-3 rounded-[var(--radius)] border border-border"><table class="w-full text-sm"><thead class="bg-muted/50"><tr>${headerCells}</tr></thead><tbody>${bodyRows}</tbody></table></div>`;
91
+ });
92
+
67
93
  // Emojis and icons (keep as is)
68
-
94
+
69
95
  // Line breaks (preserve double line breaks as paragraphs)
70
96
  html = html.replace(/\n\n/g, '</p><p class="mb-2 break-words">');
71
97
  html = html.replace(/\n/g, '<br/>');
@@ -85,7 +85,7 @@ export {
85
85
  AlertDialogTitle,
86
86
  AlertDialogTrigger
87
87
  } from "./alert-dialog";
88
- export { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger } from "./sheet";
88
+ export { Sheet, SheetBody, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger } from "./sheet";
89
89
  export { Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHandle, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger } from "./drawer";
90
90
  export { Popover, PopoverContent, PopoverTrigger } from "./popover";
91
91
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
@@ -1,48 +1,54 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { RouteMap } from './route-map';
3
- import React from 'react';
4
-
5
- const meta: Meta<typeof RouteMap> = {
6
- title: 'UI/RouteMap',
7
- component: RouteMap,
8
- render: (args) => <RouteMap {...args} />,
9
- argTypes: {
10
- travelMode: {
11
- control: 'select',
12
- options: ['DRIVING', 'WALKING', 'BICYCLING', 'TRANSIT'],
13
- },
14
- height: { control: 'text' },
15
- },
16
- };
17
-
18
- export default meta;
19
- type Story = StoryObj<typeof RouteMap>;
20
-
21
- export const Default: Story = {
22
- args: {
23
- origin: { lat: -23.5505, lng: -46.6333 }, // São Paulo Center
24
- destination: { lat: -23.5617, lng: -46.6560 }, // Avenida Paulista
25
- height: "400px",
26
- },
27
- render: (args) => (
28
- <div className="w-full max-w-4xl">
29
- <RouteMap {...args} onRouteCalculated={(dist, dur) => console.log(`Distance: ${dist}, Duration: ${dur}`)} />
30
- </div>
31
- ),
32
- };
33
-
34
- export const WithWaypoints: Story = {
35
- args: {
36
- origin: { lat: -23.5505, lng: -46.6333 },
37
- destination: { lat: -23.5617, lng: -46.6560 },
38
- waypoints: [
39
- { lat: -23.5550, lng: -46.6450 },
40
- ],
41
- height: "500px",
42
- },
43
- render: (args) => (
44
- <div className="w-full max-w-4xl">
45
- <RouteMap {...args} />
46
- </div>
47
- ),
48
- };
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { RouteMap } from './route-map';
3
+ import React from 'react';
4
+
5
+ function MapStoryFrame({ children }: { children: React.ReactNode }) {
6
+ return (
7
+ <div style={{ width: 'min(100vw - 48px, 960px)', minWidth: 320 }}>
8
+ {children}
9
+ </div>
10
+ );
11
+ }
12
+
13
+ const meta: Meta<typeof RouteMap> = {
14
+ title: 'UI/RouteMap',
15
+ component: RouteMap,
16
+ render: (args) => (
17
+ <MapStoryFrame>
18
+ <RouteMap {...args} />
19
+ </MapStoryFrame>
20
+ ),
21
+ parameters: {
22
+ layout: 'centered',
23
+ },
24
+ argTypes: {
25
+ travelMode: {
26
+ control: 'select',
27
+ options: ['DRIVING', 'WALKING', 'BICYCLING', 'TRANSIT'],
28
+ },
29
+ height: { control: 'text' },
30
+ },
31
+ };
32
+
33
+ export default meta;
34
+ type Story = StoryObj<typeof RouteMap>;
35
+
36
+ export const Default: Story = {
37
+ args: {
38
+ origin: { lat: -23.5505, lng: -46.6333 },
39
+ destination: { lat: -23.5617, lng: -46.6560 },
40
+ height: "400px",
41
+ onRouteCalculated: (dist, dur) => console.log(`Distance: ${dist}, Duration: ${dur}`),
42
+ },
43
+ };
44
+
45
+ export const WithWaypoints: Story = {
46
+ args: {
47
+ origin: { lat: -23.5505, lng: -46.6333 },
48
+ destination: { lat: -23.5617, lng: -46.6560 },
49
+ waypoints: [
50
+ { lat: -23.5550, lng: -46.6450 },
51
+ ],
52
+ height: "500px",
53
+ },
54
+ };
@@ -1,94 +1,95 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import {
3
- Sheet,
4
- SheetContent,
5
- SheetDescription,
6
- SheetFooter,
7
- SheetHeader,
8
- SheetTitle,
9
- SheetTrigger,
10
- SheetClose,
11
- } from './sheet';
12
- import { Button } from '../button';
13
- import { Input } from '../input';
14
- import { Label } from '../label';
15
- import React from 'react';
16
-
17
- const meta: Meta<typeof Sheet> = {
18
- title: 'UI/Sheet',
19
- component: Sheet,
20
- render: (args) => <Sheet {...args} />,
21
- argTypes: {
22
- open: {
23
- control: 'boolean',
24
- description: 'Whether the sheet is open.',
25
- },
26
- modal: {
27
- control: 'boolean',
28
- description: 'Whether the sheet should be modal.',
29
- defaultValue: true,
30
- },
31
- },
32
- };
33
-
34
- export default meta;
35
- type Story = StoryObj<typeof Sheet>;
36
-
37
- export const Default: Story = {
38
- render: (args) => (
39
- <Sheet {...args}>
40
- <SheetTrigger asChild>
41
- <Button variant="outline">Open Right Sheet</Button>
42
- </SheetTrigger>
43
- <SheetContent>
44
- <SheetHeader>
45
- <SheetTitle>Edit profile</SheetTitle>
46
- <SheetDescription>
47
- Make changes to your profile here. Click save when you're done.
48
- </SheetDescription>
49
- </SheetHeader>
50
- <div className="grid gap-4 py-4">
51
- <div className="grid grid-cols-4 items-center gap-4">
52
- <Label htmlFor="name" className="text-right">
53
- Name
54
- </Label>
55
- <Input id="name" value="Pedro Duarte" className="col-span-3" />
56
- </div>
57
- <div className="grid grid-cols-4 items-center gap-4">
58
- <Label htmlFor="username" className="text-right">
59
- Username
60
- </Label>
61
- <Input id="username" value="@peduarte" className="col-span-3" />
62
- </div>
63
- </div>
64
- <SheetFooter>
65
- <SheetClose asChild>
66
- <Button type="submit">Save changes</Button>
67
- </SheetClose>
68
- </SheetFooter>
69
- </SheetContent>
70
- </Sheet>
71
- ),
72
- };
73
-
74
- export const SideBySide: Story = {
75
- render: () => (
76
- <div className="grid grid-cols-2 gap-2 w-fit">
77
- {(["top", "bottom", "left", "right"] as const).map((side) => (
78
- <Sheet key={side}>
79
- <SheetTrigger asChild>
80
- <Button variant="outline">{side}</Button>
81
- </SheetTrigger>
82
- <SheetContent side={side}>
83
- <SheetHeader>
84
- <SheetTitle>Edit profile</SheetTitle>
85
- <SheetDescription>
86
- This sheet pops out from the {side}.
87
- </SheetDescription>
88
- </SheetHeader>
89
- </SheetContent>
90
- </Sheet>
91
- ))}
92
- </div>
93
- ),
94
- };
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import {
3
+ Sheet,
4
+ SheetContent,
5
+ SheetDescription,
6
+ SheetBody,
7
+ SheetFooter,
8
+ SheetHeader,
9
+ SheetTitle,
10
+ SheetTrigger,
11
+ SheetClose,
12
+ } from './sheet';
13
+ import { Button } from '../button';
14
+ import { Input } from '../input';
15
+ import { Label } from '../label';
16
+ import React from 'react';
17
+
18
+ const meta: Meta<typeof Sheet> = {
19
+ title: 'UI/Sheet',
20
+ component: Sheet,
21
+ render: (args) => <Sheet {...args} />,
22
+ argTypes: {
23
+ open: {
24
+ control: 'boolean',
25
+ description: 'Whether the sheet is open.',
26
+ },
27
+ modal: {
28
+ control: 'boolean',
29
+ description: 'Whether the sheet should be modal.',
30
+ defaultValue: true,
31
+ },
32
+ },
33
+ };
34
+
35
+ export default meta;
36
+ type Story = StoryObj<typeof Sheet>;
37
+
38
+ export const Default: Story = {
39
+ render: (args) => (
40
+ <Sheet {...args}>
41
+ <SheetTrigger asChild>
42
+ <Button variant="outline">Open Right Sheet</Button>
43
+ </SheetTrigger>
44
+ <SheetContent>
45
+ <SheetHeader>
46
+ <SheetTitle>Edit profile</SheetTitle>
47
+ <SheetDescription>
48
+ Make changes to your profile here. Click save when you're done.
49
+ </SheetDescription>
50
+ </SheetHeader>
51
+ <SheetBody className="grid gap-4">
52
+ <div className="grid grid-cols-4 items-center gap-4">
53
+ <Label htmlFor="name" className="text-right">
54
+ Name
55
+ </Label>
56
+ <Input id="name" value="Pedro Duarte" className="col-span-3" />
57
+ </div>
58
+ <div className="grid grid-cols-4 items-center gap-4">
59
+ <Label htmlFor="username" className="text-right">
60
+ Username
61
+ </Label>
62
+ <Input id="username" value="@peduarte" className="col-span-3" />
63
+ </div>
64
+ </SheetBody>
65
+ <SheetFooter>
66
+ <SheetClose asChild>
67
+ <Button type="submit">Save changes</Button>
68
+ </SheetClose>
69
+ </SheetFooter>
70
+ </SheetContent>
71
+ </Sheet>
72
+ ),
73
+ };
74
+
75
+ export const SideBySide: Story = {
76
+ render: () => (
77
+ <div className="grid grid-cols-2 gap-2 w-fit">
78
+ {(["top", "bottom", "left", "right"] as const).map((side) => (
79
+ <Sheet key={side}>
80
+ <SheetTrigger asChild>
81
+ <Button variant="outline">{side}</Button>
82
+ </SheetTrigger>
83
+ <SheetContent side={side}>
84
+ <SheetHeader>
85
+ <SheetTitle>Edit profile</SheetTitle>
86
+ <SheetDescription>
87
+ This sheet pops out from the {side}.
88
+ </SheetDescription>
89
+ </SheetHeader>
90
+ </SheetContent>
91
+ </Sheet>
92
+ ))}
93
+ </div>
94
+ ),
95
+ };
@@ -102,6 +102,16 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
102
102
  );
103
103
  }
104
104
 
105
+ function SheetBody({ className, ...props }: React.ComponentProps<"div">) {
106
+ return (
107
+ <div
108
+ data-slot="sheet-body"
109
+ className={cn("flex-1 overflow-auto px-6", className)}
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+
105
115
  function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
106
116
  return (
107
117
  <div
@@ -144,6 +154,7 @@ export {
144
154
  SheetClose,
145
155
  SheetContent,
146
156
  SheetHeader,
157
+ SheetBody,
147
158
  SheetFooter,
148
159
  SheetTitle,
149
160
  SheetDescription,
@@ -1,43 +1,48 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { SimpleMap } from './simple-map';
3
- import React from 'react';
4
-
5
- const meta: Meta<typeof SimpleMap> = {
6
- title: 'UI/SimpleMap',
7
- component: SimpleMap,
8
- render: (args) => <SimpleMap {...args} />,
9
- argTypes: {
10
- zoom: { control: { type: 'range', min: 1, max: 20 } },
11
- height: { control: 'text' },
12
- },
13
- };
14
-
15
- export default meta;
16
- type Story = StoryObj<typeof SimpleMap>;
17
-
18
- export const Default: Story = {
19
- args: {
20
- center: { lat: -23.5505, lng: -46.6333 },
21
- markerTitle: "Central Office",
22
- markerInfo: "Paulista Ave, 1000",
23
- zoom: 15,
24
- },
25
- render: (args) => (
26
- <div className="w-full max-w-2xl">
27
- <SimpleMap {...args} />
28
- </div>
29
- ),
30
- };
31
-
32
- export const WithoutMarker: Story = {
33
- args: {
34
- center: { lat: -23.5505, lng: -46.6333 },
35
- showMarker: false,
36
- zoom: 12,
37
- },
38
- render: (args) => (
39
- <div className="w-full max-w-2xl">
40
- <SimpleMap {...args} />
41
- </div>
42
- ),
43
- };
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { SimpleMap } from './simple-map';
3
+ import React from 'react';
4
+
5
+ function MapStoryFrame({ children }: { children: React.ReactNode }) {
6
+ return (
7
+ <div style={{ width: 'min(100vw - 48px, 960px)', minWidth: 320 }}>
8
+ {children}
9
+ </div>
10
+ );
11
+ }
12
+
13
+ const meta: Meta<typeof SimpleMap> = {
14
+ title: 'UI/SimpleMap',
15
+ component: SimpleMap,
16
+ render: (args) => (
17
+ <MapStoryFrame>
18
+ <SimpleMap {...args} />
19
+ </MapStoryFrame>
20
+ ),
21
+ parameters: {
22
+ layout: 'centered',
23
+ },
24
+ argTypes: {
25
+ zoom: { control: { type: 'range', min: 1, max: 20 } },
26
+ height: { control: 'text' },
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof SimpleMap>;
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ center: { lat: -23.5505, lng: -46.6333 },
36
+ markerTitle: "Central Office",
37
+ markerInfo: "Paulista Ave, 1000",
38
+ zoom: 15,
39
+ },
40
+ };
41
+
42
+ export const WithoutMarker: Story = {
43
+ args: {
44
+ center: { lat: -23.5505, lng: -46.6333 },
45
+ showMarker: false,
46
+ zoom: 12,
47
+ },
48
+ };