@softize/opus 28.0.0 → 30.0.0

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/bin/cli.mjs +68 -3
  3. package/bin/lib/check.mjs +86 -10
  4. package/bin/lib/copy.mjs +505 -67
  5. package/bin/lib/materialize.mjs +99 -1
  6. package/docs/adr/0028-schedules-run-actions-as-the-system-by-action-opt-in.md +1 -1
  7. package/docs/adr/0037-design-data-lives-outside-the-contract.md +1 -1
  8. package/docs/adr/0045-surface-replaces-content-and-card.md +3 -0
  9. package/docs/adr/0061-claude-md-imports-agents-md.md +41 -0
  10. package/docs/adr/0062-listas-contidas-e-texto-flexivel.md +68 -0
  11. package/docs/adr/0063-metadados-das-superficies.md +44 -0
  12. package/docs/adr/README.md +6 -0
  13. package/docs/code-style.md +47 -8
  14. package/docs/protocol.md +1 -1
  15. package/docs/releasing.md +7 -0
  16. package/package.json +1 -1
  17. package/registry/instructions/opus.md +3 -1
  18. package/registry/skills/build-opus-ui/SKILL.md +6 -4
  19. package/registry/skills/build-opus-ui/references/evaluations.md +10 -1
  20. package/registry/skills/build-opus-ui/references/ui-patterns.md +10 -2
  21. package/registry/skills/maintain-opus-docs/SKILL.md +2 -1
  22. package/registry/skills/maintain-opus-docs/references/editorial-standard.md +9 -0
  23. package/registry/skills/maintain-opus-docs/references/evaluations.md +8 -0
  24. package/registry/templates/app/pnpm-workspace.yaml +1 -1
  25. package/src/core/system-actor.ts +1 -1
  26. package/src/core/types.ts +1 -1
  27. package/src/ui/components/patterns/list.tsx +19 -10
  28. package/src/ui/components/patterns/page.tsx +27 -0
  29. package/src/ui/components/patterns/structural-header.tsx +25 -3
  30. package/src/ui/components/patterns/surface.tsx +36 -8
  31. package/src/ui/components/primitives/dialog.tsx +74 -42
  32. package/src/ui/components/primitives/drawer.tsx +69 -42
  33. package/src/ui/components/primitives/table.tsx +3 -3
  34. package/src/ui/docs/content/action-list.md +15 -8
  35. package/src/ui/docs/content/actions.md +1 -1
  36. package/src/ui/docs/content/auth.md +3 -3
  37. package/src/ui/docs/content/cli.md +6 -0
  38. package/src/ui/docs/content/communication.md +10 -8
  39. package/src/ui/docs/content/dialog.md +39 -0
  40. package/src/ui/docs/content/drawer.md +36 -0
  41. package/src/ui/docs/content/events.md +13 -13
  42. package/src/ui/docs/content/installation.md +4 -0
  43. package/src/ui/docs/content/log.md +1 -1
  44. package/src/ui/docs/content/page.md +30 -1
  45. package/src/ui/docs/content/queue.md +2 -2
  46. package/src/ui/docs/content/router.md +1 -1
  47. package/src/ui/docs/content/scheduler.md +15 -15
  48. package/src/ui/docs/content/surface.md +41 -8
  49. package/src/ui/docs/content/table.md +7 -0
  50. package/src/ui/docs/content/testing.md +4 -4
  51. package/src/ui/react.tsx +4 -0
  52. package/src/vite/design.ts +1 -1
@@ -1524,7 +1524,7 @@ export function ActionFilterBar({
1524
1524
  return (
1525
1525
  <TooltipProvider>
1526
1526
  <div data-slot="action-filter-bar" className="space-y-2">
1527
- <div className="flex min-w-0 items-end gap-3">
1527
+ <div className="flex min-w-0 flex-wrap items-end gap-3">
1528
1528
  {periodPosition === "before" && periodControl}
1529
1529
  {inlineSpecs.map(([name, spec]) => {
1530
1530
  if (name === periodBasisEntry?.[0]) {
@@ -1636,7 +1636,7 @@ export function ActionFilterBar({
1636
1636
  {!searchAtActionsEnd && searchBox}
1637
1637
  <div
1638
1638
  data-slot="action-filter-actions"
1639
- className="ml-auto flex min-w-0 items-end gap-2"
1639
+ className="ml-auto flex min-w-0 max-w-full flex-wrap items-end gap-2"
1640
1640
  >
1641
1641
  {searchAtActionsEnd && searchBox}
1642
1642
  {(modalSpecs.length > 0 ||
@@ -2074,7 +2074,7 @@ export function ActionList<TInput, TItem>({
2074
2074
  header: text(c.label, c.key),
2075
2075
  sortable: c.sortable === true,
2076
2076
  spec: c,
2077
- className: cn(c.fit === true && "w-px whitespace-nowrap"),
2077
+ className: cn(c.fit === true && "whitespace-nowrap wrap-normal"),
2078
2078
  cell:
2079
2079
  cells?.[c.key] ??
2080
2080
  ((item: TItem): ReactNode => {
@@ -2534,7 +2534,13 @@ export function ActionList<TInput, TItem>({
2534
2534
  </TableCell>
2535
2535
  )}
2536
2536
  {derived.map((col) => (
2537
- <TableCell key={col.key} className={col.className}>
2537
+ <TableCell
2538
+ key={col.key}
2539
+ className={cn(
2540
+ "has-[>[role=switch]:only-child]:w-0 has-[>[data-slot=tooltip-trigger]:only-child>[role=switch]:only-child]:w-0",
2541
+ col.className,
2542
+ )}
2543
+ >
2538
2544
  {col.cell(item)}
2539
2545
  </TableCell>
2540
2546
  ))}
@@ -2597,7 +2603,10 @@ export function ActionList<TInput, TItem>({
2597
2603
  </TableHead>
2598
2604
  )}
2599
2605
  {derived.map((col) => (
2600
- <TableHead key={col.key} className={col.className}>
2606
+ <TableHead
2607
+ key={col.key}
2608
+ className={cn(col.className, col.spec && "whitespace-normal")}
2609
+ >
2601
2610
  {col.sortable === true ? (
2602
2611
  <button
2603
2612
  type="button"
@@ -2959,8 +2968,8 @@ export function ActionList<TInput, TItem>({
2959
2968
  data-layout={layout}
2960
2969
  className={cn(
2961
2970
  layout === "fill"
2962
- ? "flex h-full min-h-0 flex-1 flex-col gap-3"
2963
- : "space-y-3",
2971
+ ? "flex h-full min-h-0 min-w-0 max-w-full flex-1 flex-col gap-3"
2972
+ : "min-w-0 max-w-full space-y-3",
2964
2973
  )}
2965
2974
  >
2966
2975
  {/* A lista é a folha da cadeia de altura: o que ela hospeda decide a própria. */}
@@ -2975,7 +2984,7 @@ export function ActionList<TInput, TItem>({
2975
2984
  tem altura própria e ela vira a altura do conteúdo. */}
2976
2985
  <div
2977
2986
  className={cn(
2978
- "grid min-h-0 grid-rows-[1fr_auto]",
2987
+ "grid min-h-0 min-w-0 grid-cols-[minmax(0,1fr)] grid-rows-[1fr_auto]",
2979
2988
  layout === "fill" && "flex-1",
2980
2989
  )}
2981
2990
  >
@@ -2986,7 +2995,7 @@ export function ActionList<TInput, TItem>({
2986
2995
  <div
2987
2996
  aria-busy={busy}
2988
2997
  className={cn(
2989
- "min-h-0",
2998
+ "min-h-0 min-w-0",
2990
2999
  layout === "fill" && "overflow-auto",
2991
3000
  busy && items.length > 0 && "pointer-events-none opacity-60",
2992
3001
  )}
@@ -3058,7 +3067,7 @@ export function ActionList<TInput, TItem>({
3058
3067
  // URL sync — a serialização padrão do estado da toolbar (querystring compacta)
3059
3068
  // =============================================================================
3060
3069
  //
3061
- // Convenção: `q` · `sort=chave:dir` (omitido quando é o default do contrato) ·
3070
+ // Convenção: `q` · `sort=key:dir` (omitido quando é o default do contrato) ·
3062
3071
  // `period=preset` relativo ou `period=from..to` (o custom é implícito; default
3063
3072
  // omitido) · `view` (omitida quando é a primeira) · `cols=a,b,c` (omitida no
3064
3073
  // default) · filtros pelo PRÓPRIO nome (multiple → CSV). Quem embala liga:
@@ -31,6 +31,7 @@ import {
31
31
  } from "./page-heading-context.tsx";
32
32
  import {
33
33
  StructuralHeader,
34
+ structuralMetaClasses,
34
35
  validateSurfaceIntro,
35
36
  structuralHeaderClasses,
36
37
  } from "./structural-header.tsx";
@@ -165,6 +166,8 @@ interface PageShorthandProps extends PageBaseProps {
165
166
  title: ReactNode;
166
167
  /** Orientação opcional sobre a finalidade da página; regras pertencem ao corpo. */
167
168
  description?: ReactNode;
169
+ /** Badges, datas e autoria sob a orientação. */
170
+ meta?: ReactNode;
168
171
  actions?: ReactNode;
169
172
  children: ReactNode;
170
173
  }
@@ -172,6 +175,7 @@ interface PageShorthandProps extends PageBaseProps {
172
175
  interface PageComposedProps extends PageBaseProps {
173
176
  title?: never;
174
177
  description?: never;
178
+ meta?: never;
175
179
  actions?: never;
176
180
  children: ReactNode;
177
181
  }
@@ -246,6 +250,7 @@ function hasOpaquePageBodyContent(node: ReactNode): boolean {
246
250
  export function Page({
247
251
  title,
248
252
  description,
253
+ meta,
249
254
  actions,
250
255
  layout = "flow",
251
256
  surfaceLayout = "sectioned",
@@ -377,6 +382,7 @@ export function Page({
377
382
  {description !== undefined && (
378
383
  <PageDescription>{description}</PageDescription>
379
384
  )}
385
+ {meta !== undefined && <PageMeta>{meta}</PageMeta>}
380
386
  </PageIntro>
381
387
  {children}
382
388
  </PageBody>
@@ -490,6 +496,7 @@ export function PageHeader({
490
496
  leading: [PageBack, PageNavigation],
491
497
  title: PageTitle,
492
498
  description: PageDescription,
499
+ meta: PageMeta,
493
500
  actions: PageActions,
494
501
  }}
495
502
  leadingPlacement={
@@ -542,6 +549,7 @@ export function PageIntro({
542
549
  slots={{
543
550
  title: PageTitle,
544
551
  description: PageDescription,
552
+ meta: PageMeta,
545
553
  actions: PageIntroActions,
546
554
  }}
547
555
  leadingPlacement="inline"
@@ -555,6 +563,25 @@ export function PageIntro({
555
563
  );
556
564
  }
557
565
 
566
+ /** Badges, datas e autoria abaixo do título e da orientação. */
567
+ export function PageMeta({
568
+ className,
569
+ ...props
570
+ }: HTMLAttributes<HTMLDivElement>) {
571
+ const header = useContext(PageHeaderContext);
572
+ const intro = useContext(PageIntroContext);
573
+ const inside = header || intro;
574
+ if (!inside)
575
+ throw new Error("PageMeta deve estar dentro de PageHeader ou PageIntro.");
576
+ return (
577
+ <div
578
+ data-slot="page-meta"
579
+ className={cn(structuralMetaClasses, className)}
580
+ {...props}
581
+ />
582
+ );
583
+ }
584
+
558
585
  /** Ações locais do conteúdo, distintas das ações da superfície em PageActions. */
559
586
  export function PageIntroActions({
560
587
  className,
@@ -18,6 +18,10 @@ import {
18
18
  } from "react";
19
19
  import { cn } from "../../lib/cn.ts";
20
20
 
21
+ /** Metadados contextuais, separados da orientação textual e da linha de ações. */
22
+ export const structuralMetaClasses =
23
+ "mt-3 flex min-w-0 flex-wrap items-center gap-x-3 gap-y-2 text-sm text-muted-foreground [overflow-wrap:anywhere] [&>*]:max-w-full";
24
+
21
25
  export type StructuralHeaderVariant = "page" | "section";
22
26
 
23
27
  /** As classes por slot, por hierarquia visual — PageTitle e SurfaceTitle no nível 1 são o mesmo. */
@@ -46,6 +50,7 @@ interface StructuralHeaderSlots {
46
50
  title: ElementType;
47
51
  count?: ElementType;
48
52
  description?: ElementType;
53
+ meta?: ElementType;
49
54
  actions?: ElementType;
50
55
  }
51
56
 
@@ -127,6 +132,7 @@ export function StructuralHeader({
127
132
  const counts = slots.count === undefined ? [] : of(slots.count);
128
133
  const descriptions =
129
134
  slots.description === undefined ? [] : of(slots.description);
135
+ const metas = slots.meta === undefined ? [] : of(slots.meta);
130
136
  const actionSlots = slots.actions === undefined ? [] : of(slots.actions);
131
137
  const slotTypes = [
132
138
  ...leadingTypes,
@@ -134,6 +140,7 @@ export function StructuralHeader({
134
140
  slots.title,
135
141
  slots.count,
136
142
  slots.description,
143
+ slots.meta,
137
144
  slots.actions,
138
145
  ].filter((type): type is ElementType => type !== undefined);
139
146
  const free = allowFreeContent
@@ -151,6 +158,7 @@ export function StructuralHeader({
151
158
  titles.length +
152
159
  counts.length +
153
160
  descriptions.length +
161
+ metas.length +
154
162
  actionSlots.length;
155
163
 
156
164
  if (
@@ -159,6 +167,7 @@ export function StructuralHeader({
159
167
  medias.length > 1 ||
160
168
  counts.length > 1 ||
161
169
  descriptions.length > 1 ||
170
+ metas.length > 1 ||
162
171
  actionSlots.length > 1 ||
163
172
  recognized !== nodes.length
164
173
  ) {
@@ -170,6 +179,7 @@ export function StructuralHeader({
170
179
  ...leadingTypes,
171
180
  slots.media,
172
181
  slots.description,
182
+ slots.meta,
173
183
  slots.count,
174
184
  slots.actions,
175
185
  ]
@@ -193,6 +203,10 @@ export function StructuralHeader({
193
203
  );
194
204
  }
195
205
 
206
+ if (metas.length > 0 && titles.length === 0) {
207
+ throw new Error(`${name} exige título para exibir metadados.`);
208
+ }
209
+
196
210
  const titleLine = titles.length + counts.length > 0 && (
197
211
  <Wrapper className="flex items-baseline gap-2">
198
212
  {titles}
@@ -342,11 +356,19 @@ export function StructuralHeader({
342
356
  </>
343
357
  );
344
358
 
359
+ const withMeta = (
360
+ <>
361
+ {layout}
362
+ {metas}
363
+ </>
364
+ );
365
+
345
366
  return (
346
367
  <Root
347
368
  data-slot={`${slot}-header`}
348
369
  className={cn(
349
- contentClassName === undefined &&
370
+ metas.length === 0 &&
371
+ contentClassName === undefined &&
350
372
  leadingPlacement !== "inline" &&
351
373
  leading.length === 0 &&
352
374
  actionSlots.length > 0 &&
@@ -359,13 +381,13 @@ export function StructuralHeader({
359
381
  {...props}
360
382
  >
361
383
  {contentClassName === undefined ? (
362
- layout
384
+ withMeta
363
385
  ) : (
364
386
  <Wrapper
365
387
  data-slot={`${slot}-header-content`}
366
388
  className={contentClassName}
367
389
  >
368
- {layout}
390
+ {withMeta}
369
391
  </Wrapper>
370
392
  )}
371
393
  </Root>
@@ -42,6 +42,7 @@ import { headingTags, type HeadingLevel } from "./heading-level.ts";
42
42
  import { usePageHeadingPresence } from "./page-heading-context.tsx";
43
43
  import {
44
44
  StructuralHeader,
45
+ structuralMetaClasses,
45
46
  structuralHeaderClasses,
46
47
  } from "./structural-header.tsx";
47
48
  import {
@@ -113,6 +114,8 @@ interface SurfaceHeaderShorthandProps {
113
114
  count?: number;
114
115
  /** Forma curta: frase de apoio sob o título. */
115
116
  description?: ReactNode;
117
+ /** Forma curta: badges, datas e autoria sob a orientação. */
118
+ meta?: ReactNode;
116
119
  /** Forma curta: ações sobre a superfície inteira, no extremo oposto ao título. */
117
120
  actions?: ReactNode;
118
121
  }
@@ -124,6 +127,7 @@ interface SurfaceLegendShorthandProps {
124
127
  media?: ReactNode;
125
128
  count?: never;
126
129
  description?: never;
130
+ meta?: never;
127
131
  actions?: never;
128
132
  }
129
133
 
@@ -132,6 +136,7 @@ interface SurfaceNoShorthandProps {
132
136
  media?: never;
133
137
  count?: never;
134
138
  description?: never;
139
+ meta?: never;
135
140
  actions?: never;
136
141
  }
137
142
 
@@ -204,6 +209,7 @@ export function Surface({
204
209
  media,
205
210
  count,
206
211
  description,
212
+ meta,
207
213
  actions,
208
214
  className,
209
215
  children,
@@ -230,10 +236,11 @@ export function Surface({
230
236
  (hasMedia(media) ||
231
237
  count !== undefined ||
232
238
  description !== undefined ||
239
+ meta !== undefined ||
233
240
  actions !== undefined)
234
241
  ) {
235
242
  throw new Error(
236
- "Surface exige title para usar media, count, description ou actions.",
243
+ "Surface exige title para usar media, count, description, meta ou actions.",
237
244
  );
238
245
  }
239
246
  if (asChild && shorthand) {
@@ -303,10 +310,11 @@ export function Surface({
303
310
  <SurfaceHeader>
304
311
  {hasMedia(media) && <SurfaceMedia>{media}</SurfaceMedia>}
305
312
  <SurfaceTitle>{title}</SurfaceTitle>
306
- {count !== undefined && <SurfaceMeta>{count}</SurfaceMeta>}
313
+ {count !== undefined && <SurfaceCount>{count}</SurfaceCount>}
307
314
  {description !== undefined && (
308
315
  <SurfaceDescription>{description}</SurfaceDescription>
309
316
  )}
317
+ {meta !== undefined && <SurfaceMeta>{meta}</SurfaceMeta>}
310
318
  {actions !== undefined && <SurfaceActions>{actions}</SurfaceActions>}
311
319
  </SurfaceHeader>
312
320
  {regions > 0
@@ -370,7 +378,7 @@ export function Surface({
370
378
  export type SurfaceHeaderProps = Omit<HTMLAttributes<HTMLDivElement>, "title">;
371
379
 
372
380
  const legendInvalidMessage =
373
- "SurfaceHeader de fieldset aceita somente SurfaceMedia e SurfaceTitle; contador, descrição e ações ficam fora da legenda.";
381
+ "SurfaceHeader de fieldset aceita somente SurfaceMedia e SurfaceTitle; contador, descrição, metadados e ações ficam fora da legenda.";
374
382
 
375
383
  /**
376
384
  * Header da superfície; não existe fora dela. Monta a linha sozinho — mídia, título com
@@ -401,7 +409,8 @@ export function SurfaceHeader({
401
409
  : {
402
410
  media: SurfaceMedia,
403
411
  title: SurfaceTitle,
404
- count: SurfaceMeta,
412
+ count: SurfaceCount,
413
+ meta: SurfaceMeta,
405
414
  description: SurfaceDescription,
406
415
  actions: SurfaceActions,
407
416
  }
@@ -509,25 +518,44 @@ export function SurfaceDescription({
509
518
  );
510
519
  }
511
520
 
512
- /** Metadado ao lado do título — o slot do `count` da forma curta. */
513
- export function SurfaceMeta({
521
+ /** Contador ao lado do título — o slot do `count` da forma curta. */
522
+ export function SurfaceCount({
514
523
  className,
515
524
  ...props
516
525
  }: HTMLAttributes<HTMLSpanElement>): ReactElement {
517
526
  const surface = requiredContext(
518
527
  useContext(SurfaceHeaderContext),
519
- "SurfaceMeta",
528
+ "SurfaceCount",
520
529
  "SurfaceHeader",
521
530
  );
522
531
  return (
523
532
  <span
524
- data-slot="surface-meta"
533
+ data-slot="surface-count"
525
534
  className={cn(structuralHeaderClasses[surface.variant].count, className)}
526
535
  {...props}
527
536
  />
528
537
  );
529
538
  }
530
539
 
540
+ /** Badges, datas e autoria sob o título e a orientação da superfície. */
541
+ export function SurfaceMeta({
542
+ className,
543
+ ...props
544
+ }: HTMLAttributes<HTMLDivElement>): ReactElement {
545
+ requiredContext(
546
+ useContext(SurfaceHeaderContext),
547
+ "SurfaceMeta",
548
+ "SurfaceHeader",
549
+ );
550
+ return (
551
+ <div
552
+ data-slot="surface-meta"
553
+ className={cn(structuralMetaClasses, className)}
554
+ {...props}
555
+ />
556
+ );
557
+ }
558
+
531
559
  /** Ações do header — a família as posiciona no extremo oposto ao título. */
532
560
  export function SurfaceActions({
533
561
  className,
@@ -16,6 +16,7 @@ import { FillBoundary } from "../patterns/fill-region.ts";
16
16
  import { headingTags, type HeadingLevel } from "../patterns/heading-level.ts";
17
17
  import {
18
18
  StructuralHeader,
19
+ structuralMetaClasses,
19
20
  structuralHeaderClasses,
20
21
  validateSurfaceIntro,
21
22
  } from "../patterns/structural-header.tsx";
@@ -67,6 +68,7 @@ const DialogContentContext = React.createContext<DialogContentContextValue>({
67
68
  closeSize: "sm",
68
69
  });
69
70
 
71
+ const DialogMetaContext = React.createContext(false);
70
72
  const DialogIntroContext = React.createContext(false);
71
73
 
72
74
  /**
@@ -589,33 +591,36 @@ export function DialogHeader({
589
591
  }
590
592
 
591
593
  return (
592
- <StructuralHeader
593
- name="DialogHeader"
594
- slot="dialog"
595
- slots={{
596
- leading: DialogNavigation,
597
- media: DialogMedia,
598
- title: DialogTitle,
599
- description: DialogDescription,
600
- actions: DialogActions,
601
- }}
602
- leadingPlacement="inline"
603
- titleRequired={false}
604
- rowClassName={surfaceHeaderRowClasses("dialog")}
605
- className={cn(
606
- "text-left",
607
- surfaceRegionClasses("dialog", layout, "header"),
608
- className,
609
- )}
610
- trailing={
611
- showCloseButton ? (
612
- <DialogCloseButton disabled={closeDisabled} size={closeSize} />
613
- ) : undefined
614
- }
615
- {...props}
616
- >
617
- {children}
618
- </StructuralHeader>
594
+ <DialogMetaContext.Provider value>
595
+ <StructuralHeader
596
+ name="DialogHeader"
597
+ slot="dialog"
598
+ slots={{
599
+ leading: DialogNavigation,
600
+ media: DialogMedia,
601
+ title: DialogTitle,
602
+ description: DialogDescription,
603
+ meta: DialogMeta,
604
+ actions: DialogActions,
605
+ }}
606
+ leadingPlacement="inline"
607
+ titleRequired={false}
608
+ rowClassName={surfaceHeaderRowClasses("dialog")}
609
+ className={cn(
610
+ "text-left",
611
+ surfaceRegionClasses("dialog", layout, "header"),
612
+ className,
613
+ )}
614
+ trailing={
615
+ showCloseButton ? (
616
+ <DialogCloseButton disabled={closeDisabled} size={closeSize} />
617
+ ) : undefined
618
+ }
619
+ {...props}
620
+ >
621
+ {children}
622
+ </StructuralHeader>
623
+ </DialogMetaContext.Provider>
619
624
  );
620
625
  }
621
626
 
@@ -655,25 +660,52 @@ export function DialogIntro({
655
660
  }: React.ComponentProps<"div">) {
656
661
  return (
657
662
  <DialogIntroContext.Provider value>
658
- <StructuralHeader
659
- name="DialogIntro"
660
- slot="dialog-intro"
661
- slots={{
662
- title: DialogTitle,
663
- description: DialogDescription,
664
- actions: DialogIntroActions,
665
- }}
666
- leadingPlacement="inline"
667
- titleRequired
668
- className={cn("text-left", className)}
669
- {...props}
670
- >
671
- {children}
672
- </StructuralHeader>
663
+ <DialogMetaContext.Provider value>
664
+ <StructuralHeader
665
+ name="DialogIntro"
666
+ slot="dialog-intro"
667
+ slots={{
668
+ title: DialogTitle,
669
+ description: DialogDescription,
670
+ meta: DialogMeta,
671
+ actions: DialogIntroActions,
672
+ }}
673
+ leadingPlacement="inline"
674
+ titleRequired
675
+ className={cn("text-left", className)}
676
+ {...props}
677
+ >
678
+ {children}
679
+ </StructuralHeader>
680
+ </DialogMetaContext.Provider>
673
681
  </DialogIntroContext.Provider>
674
682
  );
675
683
  }
676
684
 
685
+ /** Badges, datas e autoria abaixo do título e da orientação. */
686
+ export function DialogMeta({
687
+ className,
688
+ ...props
689
+ }: React.ComponentProps<"div">) {
690
+ const { mode } = useDialogContext();
691
+ const inside = React.useContext(DialogMetaContext);
692
+ if (mode === "alert")
693
+ throw new Error(
694
+ 'DialogMeta não está disponível em Dialog com mode="alert"; coloque as informações no corpo do alerta.',
695
+ );
696
+ if (!inside)
697
+ throw new Error(
698
+ "DialogMeta deve estar dentro de DialogHeader ou DialogIntro.",
699
+ );
700
+ return (
701
+ <div
702
+ data-slot="dialog-meta"
703
+ className={cn(structuralMetaClasses, className)}
704
+ {...props}
705
+ />
706
+ );
707
+ }
708
+
677
709
  /** Ações relacionadas ao título do conteúdo, separadas das ações de chrome do header. */
678
710
  export function DialogIntroActions({
679
711
  className,