@softize/opus 8.6.6
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 +1616 -0
- package/LICENSE +21 -0
- package/README.md +113 -0
- package/bin/cli.mjs +528 -0
- package/bin/lib/check.mjs +307 -0
- package/bin/lib/components.mjs +151 -0
- package/bin/lib/create.mjs +208 -0
- package/bin/lib/db-check-runner.mjs +86 -0
- package/bin/lib/db-migrate-runner.mjs +89 -0
- package/bin/lib/db-scaffold-runner.mjs +84 -0
- package/bin/lib/db.mjs +261 -0
- package/bin/lib/docs-include.mjs +48 -0
- package/bin/lib/gen-dicts.mjs +134 -0
- package/bin/lib/gen-docs.mjs +288 -0
- package/bin/lib/gen-manifest.mjs +102 -0
- package/bin/lib/gen-openapi.mjs +195 -0
- package/bin/lib/gen-runner.mjs +472 -0
- package/bin/lib/gen-stubs.mjs +463 -0
- package/bin/lib/gen.mjs +311 -0
- package/bin/lib/init.mjs +514 -0
- package/bin/lib/introspect.mjs +107 -0
- package/bin/lib/mcp.mjs +85 -0
- package/bin/lib/postinstall.mjs +56 -0
- package/docs/chat-event-protocol.md +85 -0
- package/docs/code-style.md +16 -0
- package/docs/data-layer.md +246 -0
- package/docs/ownership-vs-shadcn-lock.md +102 -0
- package/docs/protocol.md +2053 -0
- package/docs/releasing.md +110 -0
- package/docs/shellnav.md +131 -0
- package/package.json +338 -0
- package/registry/hooks/hooks.json +26 -0
- package/registry/hooks/link-memory-on-start.mjs +46 -0
- package/registry/hooks/opus-check-on-stop.mjs +114 -0
- package/registry/skills/create-action/SKILL.md +49 -0
- package/registry/skills/create-action/scaffold.mjs +122 -0
- package/registry/templates/app/_gitignore +3 -0
- package/registry/templates/app/_npmrc +1 -0
- package/registry/templates/app/_opus/_gitignore +5 -0
- package/registry/templates/app/_prettierrc.json +6 -0
- package/registry/templates/app/index.html +13 -0
- package/registry/templates/app/opus.config.ts +16 -0
- package/registry/templates/app/package.json +43 -0
- package/registry/templates/app/pnpm-workspace.yaml +11 -0
- package/registry/templates/app/public/favicon.svg +4 -0
- package/registry/templates/app/src/App.tsx +37 -0
- package/registry/templates/app/src/domains/tasks/actions/list.test.ts +34 -0
- package/registry/templates/app/src/domains/tasks/actions/list.ts +33 -0
- package/registry/templates/app/src/domains/tasks/index.ts +13 -0
- package/registry/templates/app/src/index.css +18 -0
- package/registry/templates/app/src/main.tsx +25 -0
- package/registry/templates/app/tsconfig.json +20 -0
- package/registry/templates/app/vite.config.ts +46 -0
- package/registry/templates/monorepo/_gitignore +3 -0
- package/registry/templates/monorepo/_npmrc +1 -0
- package/registry/templates/monorepo/package.json +9 -0
- package/registry/templates/monorepo/pnpm-workspace.yaml +14 -0
- package/src/ai/ask.ts +64 -0
- package/src/ai/drivers/anthropic.ts +309 -0
- package/src/ai/index.ts +17 -0
- package/src/audit/drivers/console.ts +117 -0
- package/src/audit/drivers/pg.ts +172 -0
- package/src/audit/index.ts +51 -0
- package/src/auth/drivers/better-auth.ts +103 -0
- package/src/auth/drivers/jwt.ts +188 -0
- package/src/auth/index.ts +9 -0
- package/src/client/drivers/fetch.ts +202 -0
- package/src/client/index.ts +22 -0
- package/src/core/actions.ts +110 -0
- package/src/core/audit.ts +239 -0
- package/src/core/contracts.ts +137 -0
- package/src/core/domain.ts +310 -0
- package/src/core/errors.ts +181 -0
- package/src/core/index.ts +174 -0
- package/src/core/logical-type.ts +31 -0
- package/src/core/reactions.ts +81 -0
- package/src/core/runtime.ts +1167 -0
- package/src/core/schedules.ts +41 -0
- package/src/core/types.ts +1356 -0
- package/src/data/drivers/kysely.ts +389 -0
- package/src/data/index.ts +10 -0
- package/src/data/readonly-pool.ts +160 -0
- package/src/dsl/eval.ts +136 -0
- package/src/dsl/index.ts +29 -0
- package/src/dsl/kysely.ts +230 -0
- package/src/dsl/loads.ts +123 -0
- package/src/dsl/parser.ts +423 -0
- package/src/dsl/types.ts +113 -0
- package/src/events/drivers/mitt.ts +70 -0
- package/src/events/index.ts +9 -0
- package/src/log/drivers/pino.ts +57 -0
- package/src/log/index.ts +9 -0
- package/src/mcp/index.ts +62 -0
- package/src/queue/drivers/bullmq.ts +190 -0
- package/src/queue/index.ts +9 -0
- package/src/scheduler/drivers/node-cron.ts +93 -0
- package/src/scheduler/every.ts +45 -0
- package/src/scheduler/index.ts +9 -0
- package/src/schema/drivers/zod.ts +765 -0
- package/src/schema/entity.ts +439 -0
- package/src/schema/format/locale.ts +144 -0
- package/src/schema/index.ts +65 -0
- package/src/schema/openapi.ts +302 -0
- package/src/schema/scaffold.ts +160 -0
- package/src/server/drivers/fastify.ts +224 -0
- package/src/server/drivers/node.ts +386 -0
- package/src/server/index.ts +142 -0
- package/src/storage/drivers/fs.ts +90 -0
- package/src/storage/drivers/s3.ts +117 -0
- package/src/storage/index.ts +27 -0
- package/src/testing/fake.ts +298 -0
- package/src/testing/index.ts +324 -0
- package/src/ui/components/patterns/action-form-card.tsx +48 -0
- package/src/ui/components/patterns/action-list-dialog.tsx +93 -0
- package/src/ui/components/patterns/app-shell.tsx +227 -0
- package/src/ui/components/patterns/confirm.tsx +226 -0
- package/src/ui/components/patterns/data-state.tsx +75 -0
- package/src/ui/components/patterns/form-dialog.tsx +64 -0
- package/src/ui/components/patterns/form.tsx +584 -0
- package/src/ui/components/patterns/list.tsx +1488 -0
- package/src/ui/components/patterns/page.tsx +46 -0
- package/src/ui/components/patterns/section-shell.tsx +246 -0
- package/src/ui/components/patterns/shell-nav.tsx +150 -0
- package/src/ui/components/patterns/sidebar.tsx +89 -0
- package/src/ui/components/patterns/split.tsx +93 -0
- package/src/ui/components/patterns/trigger.tsx +196 -0
- package/src/ui/components/patterns/view.tsx +84 -0
- package/src/ui/components/primitives/accordion.tsx +64 -0
- package/src/ui/components/primitives/alert-dialog.tsx +190 -0
- package/src/ui/components/primitives/alert.tsx +116 -0
- package/src/ui/components/primitives/aspect-ratio.tsx +9 -0
- package/src/ui/components/primitives/avatar.tsx +107 -0
- package/src/ui/components/primitives/badge.tsx +37 -0
- package/src/ui/components/primitives/breadcrumb.tsx +109 -0
- package/src/ui/components/primitives/button-group.tsx +83 -0
- package/src/ui/components/primitives/button.tsx +102 -0
- package/src/ui/components/primitives/calendar.tsx +218 -0
- package/src/ui/components/primitives/card.tsx +56 -0
- package/src/ui/components/primitives/carousel.tsx +239 -0
- package/src/ui/components/primitives/chat.tsx +407 -0
- package/src/ui/components/primitives/checkbox.tsx +30 -0
- package/src/ui/components/primitives/collapsible.tsx +31 -0
- package/src/ui/components/primitives/command.tsx +182 -0
- package/src/ui/components/primitives/composer.tsx +121 -0
- package/src/ui/components/primitives/copyable.tsx +50 -0
- package/src/ui/components/primitives/dialog.tsx +147 -0
- package/src/ui/components/primitives/drawer.tsx +141 -0
- package/src/ui/components/primitives/empty.tsx +104 -0
- package/src/ui/components/primitives/field.tsx +246 -0
- package/src/ui/components/primitives/icon-picker.tsx +180 -0
- package/src/ui/components/primitives/input-group.tsx +168 -0
- package/src/ui/components/primitives/input-otp.tsx +75 -0
- package/src/ui/components/primitives/input.tsx +72 -0
- package/src/ui/components/primitives/item.tsx +193 -0
- package/src/ui/components/primitives/kbd.tsx +28 -0
- package/src/ui/components/primitives/label.tsx +22 -0
- package/src/ui/components/primitives/markdown.tsx +35 -0
- package/src/ui/components/primitives/menu.tsx +255 -0
- package/src/ui/components/primitives/pagination.tsx +127 -0
- package/src/ui/components/primitives/popover.tsx +87 -0
- package/src/ui/components/primitives/progress.tsx +29 -0
- package/src/ui/components/primitives/radio-group.tsx +43 -0
- package/src/ui/components/primitives/resizable.tsx +51 -0
- package/src/ui/components/primitives/scroll-area.tsx +56 -0
- package/src/ui/components/primitives/select.tsx +479 -0
- package/src/ui/components/primitives/separator.tsx +26 -0
- package/src/ui/components/primitives/skeleton.tsx +13 -0
- package/src/ui/components/primitives/slider.tsx +61 -0
- package/src/ui/components/primitives/sonner.tsx +46 -0
- package/src/ui/components/primitives/spinner.tsx +29 -0
- package/src/ui/components/primitives/switch.tsx +33 -0
- package/src/ui/components/primitives/table.tsx +114 -0
- package/src/ui/components/primitives/tabs.tsx +104 -0
- package/src/ui/components/primitives/textarea.tsx +18 -0
- package/src/ui/components/primitives/toggle-group.tsx +81 -0
- package/src/ui/components/primitives/toggle.tsx +45 -0
- package/src/ui/components/primitives/tooltip.tsx +55 -0
- package/src/ui/components/primitives/truncate.tsx +49 -0
- package/src/ui/docs/DocBrowser.tsx +90 -0
- package/src/ui/docs/changelog.tsx +80 -0
- package/src/ui/docs/content/accordion.md +86 -0
- package/src/ui/docs/content/action-form-card.md +24 -0
- package/src/ui/docs/content/action-form-dialog.md +30 -0
- package/src/ui/docs/content/action-form.md +125 -0
- package/src/ui/docs/content/action-list-dialog.md +68 -0
- package/src/ui/docs/content/action-list.md +194 -0
- package/src/ui/docs/content/action-trigger.md +72 -0
- package/src/ui/docs/content/action-view.md +47 -0
- package/src/ui/docs/content/actions.md +138 -0
- package/src/ui/docs/content/ai.md +112 -0
- package/src/ui/docs/content/alert-dialog.md +73 -0
- package/src/ui/docs/content/alert.md +69 -0
- package/src/ui/docs/content/app-shell.md +155 -0
- package/src/ui/docs/content/aspect-ratio.md +66 -0
- package/src/ui/docs/content/audit.md +84 -0
- package/src/ui/docs/content/auth.md +70 -0
- package/src/ui/docs/content/avatar.md +94 -0
- package/src/ui/docs/content/badge.md +48 -0
- package/src/ui/docs/content/breadcrumb.md +87 -0
- package/src/ui/docs/content/button-group.md +71 -0
- package/src/ui/docs/content/button.md +60 -0
- package/src/ui/docs/content/calendar.md +62 -0
- package/src/ui/docs/content/card.md +49 -0
- package/src/ui/docs/content/carousel.md +85 -0
- package/src/ui/docs/content/chat.md +69 -0
- package/src/ui/docs/content/checkbox.md +75 -0
- package/src/ui/docs/content/cli.md +58 -0
- package/src/ui/docs/content/collapsible.md +64 -0
- package/src/ui/docs/content/command.md +56 -0
- package/src/ui/docs/content/composer.md +50 -0
- package/src/ui/docs/content/confirm.md +120 -0
- package/src/ui/docs/content/copyable.md +30 -0
- package/src/ui/docs/content/customization.md +110 -0
- package/src/ui/docs/content/cycle.md +34 -0
- package/src/ui/docs/content/data-state.md +47 -0
- package/src/ui/docs/content/data.md +99 -0
- package/src/ui/docs/content/dialog.md +60 -0
- package/src/ui/docs/content/drawer.md +55 -0
- package/src/ui/docs/content/empty.md +66 -0
- package/src/ui/docs/content/events.md +61 -0
- package/src/ui/docs/content/field.md +58 -0
- package/src/ui/docs/content/getting-started.md +109 -0
- package/src/ui/docs/content/icon-picker.md +51 -0
- package/src/ui/docs/content/input-group.md +78 -0
- package/src/ui/docs/content/input-otp.md +72 -0
- package/src/ui/docs/content/input.md +78 -0
- package/src/ui/docs/content/item.md +84 -0
- package/src/ui/docs/content/kbd.md +62 -0
- package/src/ui/docs/content/label.md +32 -0
- package/src/ui/docs/content/log.md +55 -0
- package/src/ui/docs/content/markdown.md +41 -0
- package/src/ui/docs/content/mcp.md +44 -0
- package/src/ui/docs/content/menu.md +114 -0
- package/src/ui/docs/content/microcopy.md +83 -0
- package/src/ui/docs/content/page.md +34 -0
- package/src/ui/docs/content/pagination.md +99 -0
- package/src/ui/docs/content/popover.md +49 -0
- package/src/ui/docs/content/progress.md +69 -0
- package/src/ui/docs/content/queue.md +62 -0
- package/src/ui/docs/content/radio-group.md +77 -0
- package/src/ui/docs/content/resizable.md +86 -0
- package/src/ui/docs/content/router.md +56 -0
- package/src/ui/docs/content/runtime.md +77 -0
- package/src/ui/docs/content/scheduler.md +66 -0
- package/src/ui/docs/content/scroll-area.md +89 -0
- package/src/ui/docs/content/section-shell.md +121 -0
- package/src/ui/docs/content/select.md +342 -0
- package/src/ui/docs/content/separator.md +33 -0
- package/src/ui/docs/content/sidebar.md +38 -0
- package/src/ui/docs/content/skeleton.md +34 -0
- package/src/ui/docs/content/slider.md +64 -0
- package/src/ui/docs/content/spinner.md +37 -0
- package/src/ui/docs/content/split.md +33 -0
- package/src/ui/docs/content/storage.md +69 -0
- package/src/ui/docs/content/switch.md +69 -0
- package/src/ui/docs/content/table.md +102 -0
- package/src/ui/docs/content/tabs.md +94 -0
- package/src/ui/docs/content/testing.md +89 -0
- package/src/ui/docs/content/textarea.md +30 -0
- package/src/ui/docs/content/toast.md +67 -0
- package/src/ui/docs/content/toggle-group.md +81 -0
- package/src/ui/docs/content/toggle.md +72 -0
- package/src/ui/docs/content/tokens.md +171 -0
- package/src/ui/docs/content/tooltip.md +50 -0
- package/src/ui/docs/content/truncate.md +37 -0
- package/src/ui/docs/content/ui.md +40 -0
- package/src/ui/docs/content/upgrading.md +48 -0
- package/src/ui/docs/doc-client.tsx +214 -0
- package/src/ui/docs/doc.tsx +301 -0
- package/src/ui/docs/folder.tsx +149 -0
- package/src/ui/docs/index.ts +21 -0
- package/src/ui/docs/markdown.tsx +130 -0
- package/src/ui/docs/md-raw.d.ts +4 -0
- package/src/ui/docs/plugin.ts +104 -0
- package/src/ui/docs/registry.tsx +424 -0
- package/src/ui/docs/standalone.tsx +107 -0
- package/src/ui/drivers/react.tsx +627 -0
- package/src/ui/index.ts +92 -0
- package/src/ui/lib/cn.ts +10 -0
- package/src/ui/lib/zod-pt-br.ts +38 -0
- package/src/ui/meta.ts +412 -0
- package/src/ui/react.tsx +235 -0
- package/src/ui/router.ts +96 -0
- package/src/ui/theme.css +234 -0
- package/src/vite/design.ts +652 -0
- package/src/vite/index.ts +8 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Switch as SwitchPrimitive } from "radix-ui"
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/cn.ts'
|
|
5
|
+
|
|
6
|
+
function Switch({
|
|
7
|
+
className,
|
|
8
|
+
size = "default",
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
|
|
11
|
+
size?: "sm" | "default"
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<SwitchPrimitive.Root
|
|
15
|
+
data-slot="switch"
|
|
16
|
+
data-size={size}
|
|
17
|
+
className={cn(
|
|
18
|
+
"peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
<SwitchPrimitive.Thumb
|
|
24
|
+
data-slot="switch-thumb"
|
|
25
|
+
className={cn(
|
|
26
|
+
"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground"
|
|
27
|
+
)}
|
|
28
|
+
/>
|
|
29
|
+
</SwitchPrimitive.Root>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { Switch }
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/cn.ts'
|
|
4
|
+
|
|
5
|
+
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="table-container"
|
|
9
|
+
className="relative w-full overflow-x-auto"
|
|
10
|
+
>
|
|
11
|
+
<table
|
|
12
|
+
data-slot="table"
|
|
13
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|
21
|
+
return (
|
|
22
|
+
<thead
|
|
23
|
+
data-slot="table-header"
|
|
24
|
+
className={cn("[&_tr]:border-b", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
31
|
+
return (
|
|
32
|
+
<tbody
|
|
33
|
+
data-slot="table-body"
|
|
34
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
41
|
+
return (
|
|
42
|
+
<tfoot
|
|
43
|
+
data-slot="table-footer"
|
|
44
|
+
className={cn(
|
|
45
|
+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
54
|
+
return (
|
|
55
|
+
<tr
|
|
56
|
+
data-slot="table-row"
|
|
57
|
+
className={cn(
|
|
58
|
+
"border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
67
|
+
return (
|
|
68
|
+
<th
|
|
69
|
+
data-slot="table-head"
|
|
70
|
+
className={cn(
|
|
71
|
+
"h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
80
|
+
return (
|
|
81
|
+
<td
|
|
82
|
+
data-slot="table-cell"
|
|
83
|
+
className={cn(
|
|
84
|
+
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function TableCaption({
|
|
93
|
+
className,
|
|
94
|
+
...props
|
|
95
|
+
}: React.ComponentProps<"caption">) {
|
|
96
|
+
return (
|
|
97
|
+
<caption
|
|
98
|
+
data-slot="table-caption"
|
|
99
|
+
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
Table,
|
|
107
|
+
TableHeader,
|
|
108
|
+
TableBody,
|
|
109
|
+
TableFooter,
|
|
110
|
+
TableHead,
|
|
111
|
+
TableRow,
|
|
112
|
+
TableCell,
|
|
113
|
+
TableCaption,
|
|
114
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Tabs as TabsPrimitive } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/cn.ts'
|
|
6
|
+
|
|
7
|
+
// Divergência da casa (ejetado): o Tabs ganhou `size` — Button e Select têm `sm`, o Tabs
|
|
8
|
+
// não tinha, e uma fileira densa (o header do Maestro) ficava com o segmento 4px mais alto
|
|
9
|
+
// que os irmãos. O tamanho flui pra TabsList via contexto (a altura é dela).
|
|
10
|
+
type TabsSize = "default" | "sm"
|
|
11
|
+
const TabsSizeContext = React.createContext<TabsSize>("default")
|
|
12
|
+
|
|
13
|
+
function Tabs({
|
|
14
|
+
className,
|
|
15
|
+
orientation = "horizontal",
|
|
16
|
+
size = "default",
|
|
17
|
+
...props
|
|
18
|
+
}: React.ComponentProps<typeof TabsPrimitive.Root> & { size?: TabsSize }) {
|
|
19
|
+
return (
|
|
20
|
+
<TabsSizeContext.Provider value={size}>
|
|
21
|
+
<TabsPrimitive.Root
|
|
22
|
+
data-slot="tabs"
|
|
23
|
+
data-orientation={orientation}
|
|
24
|
+
data-size={size}
|
|
25
|
+
orientation={orientation}
|
|
26
|
+
className={cn(
|
|
27
|
+
"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
</TabsSizeContext.Provider>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const tabsListVariants = cva(
|
|
37
|
+
// Altura: horizontal segue o size (h-9 default / h-8 sm — combina `data-[size]` da PRÓPRIA
|
|
38
|
+
// list com o `group-data-[orientation]` do ancestral); vertical é h-fit em qualquer size.
|
|
39
|
+
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:data-[size=default]:h-9 group-data-[orientation=horizontal]/tabs:data-[size=sm]:h-8 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
|
|
40
|
+
{
|
|
41
|
+
variants: {
|
|
42
|
+
variant: {
|
|
43
|
+
default: "bg-muted",
|
|
44
|
+
line: "gap-1 bg-transparent",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
defaultVariants: {
|
|
48
|
+
variant: "default",
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
function TabsList({
|
|
54
|
+
className,
|
|
55
|
+
variant = "default",
|
|
56
|
+
...props
|
|
57
|
+
}: React.ComponentProps<typeof TabsPrimitive.List> &
|
|
58
|
+
VariantProps<typeof tabsListVariants>) {
|
|
59
|
+
// O size vem do <Tabs> (contexto) — a altura mora aqui, na list.
|
|
60
|
+
const size = React.useContext(TabsSizeContext)
|
|
61
|
+
return (
|
|
62
|
+
<TabsPrimitive.List
|
|
63
|
+
data-slot="tabs-list"
|
|
64
|
+
data-variant={variant}
|
|
65
|
+
data-size={size}
|
|
66
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function TabsTrigger({
|
|
73
|
+
className,
|
|
74
|
+
...props
|
|
75
|
+
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
|
76
|
+
return (
|
|
77
|
+
<TabsPrimitive.Trigger
|
|
78
|
+
data-slot="tabs-trigger"
|
|
79
|
+
className={cn(
|
|
80
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
81
|
+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
|
|
82
|
+
"data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
|
|
83
|
+
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function TabsContent({
|
|
92
|
+
className,
|
|
93
|
+
...props
|
|
94
|
+
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
|
95
|
+
return (
|
|
96
|
+
<TabsPrimitive.Content
|
|
97
|
+
data-slot="tabs-content"
|
|
98
|
+
className={cn("flex-1 outline-none", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from '../../lib/cn.ts'
|
|
4
|
+
|
|
5
|
+
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
|
+
return (
|
|
7
|
+
<textarea
|
|
8
|
+
data-slot="textarea"
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Textarea }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/cn.ts'
|
|
6
|
+
import { toggleVariants } from './toggle.tsx'
|
|
7
|
+
|
|
8
|
+
const ToggleGroupContext = React.createContext<
|
|
9
|
+
VariantProps<typeof toggleVariants> & {
|
|
10
|
+
spacing?: number
|
|
11
|
+
}
|
|
12
|
+
>({
|
|
13
|
+
size: "default",
|
|
14
|
+
variant: "default",
|
|
15
|
+
spacing: 0,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
function ToggleGroup({
|
|
19
|
+
className,
|
|
20
|
+
variant,
|
|
21
|
+
size,
|
|
22
|
+
spacing = 0,
|
|
23
|
+
children,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
|
|
26
|
+
VariantProps<typeof toggleVariants> & {
|
|
27
|
+
spacing?: number
|
|
28
|
+
}) {
|
|
29
|
+
return (
|
|
30
|
+
<ToggleGroupPrimitive.Root
|
|
31
|
+
data-slot="toggle-group"
|
|
32
|
+
data-variant={variant}
|
|
33
|
+
data-size={size}
|
|
34
|
+
data-spacing={spacing}
|
|
35
|
+
style={{ "--gap": spacing } as React.CSSProperties}
|
|
36
|
+
className={cn(
|
|
37
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md",
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
>
|
|
42
|
+
<ToggleGroupContext.Provider value={{ variant, size, spacing }}>
|
|
43
|
+
{children}
|
|
44
|
+
</ToggleGroupContext.Provider>
|
|
45
|
+
</ToggleGroupPrimitive.Root>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function ToggleGroupItem({
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
variant,
|
|
53
|
+
size,
|
|
54
|
+
...props
|
|
55
|
+
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
|
|
56
|
+
VariantProps<typeof toggleVariants>) {
|
|
57
|
+
const context = React.useContext(ToggleGroupContext)
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<ToggleGroupPrimitive.Item
|
|
61
|
+
data-slot="toggle-group-item"
|
|
62
|
+
data-variant={context.variant || variant}
|
|
63
|
+
data-size={context.size || size}
|
|
64
|
+
data-spacing={context.spacing}
|
|
65
|
+
className={cn(
|
|
66
|
+
toggleVariants({
|
|
67
|
+
variant: context.variant || variant,
|
|
68
|
+
size: context.size || size,
|
|
69
|
+
}),
|
|
70
|
+
"w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10",
|
|
71
|
+
"data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",
|
|
72
|
+
className
|
|
73
|
+
)}
|
|
74
|
+
{...props}
|
|
75
|
+
>
|
|
76
|
+
{children}
|
|
77
|
+
</ToggleGroupPrimitive.Item>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { ToggleGroup, ToggleGroupItem }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Toggle as TogglePrimitive } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../lib/cn.ts'
|
|
6
|
+
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-transparent",
|
|
13
|
+
outline:
|
|
14
|
+
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
default: "h-9 min-w-9 px-2",
|
|
18
|
+
sm: "h-8 min-w-8 px-1.5",
|
|
19
|
+
lg: "h-10 min-w-10 px-2.5",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "default",
|
|
24
|
+
size: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
function Toggle({
|
|
30
|
+
className,
|
|
31
|
+
variant,
|
|
32
|
+
size,
|
|
33
|
+
...props
|
|
34
|
+
}: React.ComponentProps<typeof TogglePrimitive.Root> &
|
|
35
|
+
VariantProps<typeof toggleVariants>) {
|
|
36
|
+
return (
|
|
37
|
+
<TogglePrimitive.Root
|
|
38
|
+
data-slot="toggle"
|
|
39
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Toggle, toggleVariants }
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui"
|
|
3
|
+
|
|
4
|
+
import { cn } from '../../lib/cn.ts'
|
|
5
|
+
|
|
6
|
+
function TooltipProvider({
|
|
7
|
+
delayDuration = 0,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
10
|
+
return (
|
|
11
|
+
<TooltipPrimitive.Provider
|
|
12
|
+
data-slot="tooltip-provider"
|
|
13
|
+
delayDuration={delayDuration}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Tooltip({
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
22
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function TooltipTrigger({
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
28
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function TooltipContent({
|
|
32
|
+
className,
|
|
33
|
+
sideOffset = 0,
|
|
34
|
+
children,
|
|
35
|
+
...props
|
|
36
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
37
|
+
return (
|
|
38
|
+
<TooltipPrimitive.Portal>
|
|
39
|
+
<TooltipPrimitive.Content
|
|
40
|
+
data-slot="tooltip-content"
|
|
41
|
+
sideOffset={sideOffset}
|
|
42
|
+
className={cn(
|
|
43
|
+
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
44
|
+
className
|
|
45
|
+
)}
|
|
46
|
+
{...props}
|
|
47
|
+
>
|
|
48
|
+
{children}
|
|
49
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
|
|
50
|
+
</TooltipPrimitive.Content>
|
|
51
|
+
</TooltipPrimitive.Portal>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from './tooltip.tsx'
|
|
3
|
+
import { cn } from '../../lib/cn.ts'
|
|
4
|
+
|
|
5
|
+
export interface TruncateProps extends React.ComponentProps<'span'> {
|
|
6
|
+
/** Conteúdo do tooltip quando transborda. Default: os próprios children. */
|
|
7
|
+
tooltip?: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Texto truncado com tooltip SÓ quando transborda de verdade (medição do overflow:
|
|
12
|
+
* `scrollWidth > clientWidth`, re-medido em resize). Substitui a composição
|
|
13
|
+
* `block truncate` + `title` sempre presente — title em texto que não corta é ruído.
|
|
14
|
+
* Requer `<TooltipProvider>` na raiz do app (o esqueleto do `opus create` já monta).
|
|
15
|
+
*/
|
|
16
|
+
export function Truncate({ tooltip, children, className, ...props }: TruncateProps): React.ReactElement {
|
|
17
|
+
const ref = React.useRef<HTMLSpanElement>(null)
|
|
18
|
+
const [overflowing, setOverflowing] = React.useState(false)
|
|
19
|
+
|
|
20
|
+
const measure = React.useCallback(() => {
|
|
21
|
+
const el = ref.current
|
|
22
|
+
if (el === null) return
|
|
23
|
+
setOverflowing(el.scrollWidth > el.clientWidth)
|
|
24
|
+
}, [])
|
|
25
|
+
|
|
26
|
+
React.useLayoutEffect(() => {
|
|
27
|
+
measure()
|
|
28
|
+
const el = ref.current
|
|
29
|
+
if (el === null || typeof ResizeObserver === 'undefined') return
|
|
30
|
+
const ro = new ResizeObserver(measure)
|
|
31
|
+
ro.observe(el)
|
|
32
|
+
return () => ro.disconnect()
|
|
33
|
+
// children na dependência: texto novo re-mede (o RO só vê mudança de CAIXA).
|
|
34
|
+
}, [measure, children])
|
|
35
|
+
|
|
36
|
+
const span = (
|
|
37
|
+
<span ref={ref} data-slot="truncate" className={cn('block truncate', className)} {...props}>
|
|
38
|
+
{children}
|
|
39
|
+
</span>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if (!overflowing) return span
|
|
43
|
+
return (
|
|
44
|
+
<Tooltip>
|
|
45
|
+
<TooltipTrigger asChild>{span}</TooltipTrigger>
|
|
46
|
+
<TooltipContent>{tooltip ?? children}</TooltipContent>
|
|
47
|
+
</Tooltip>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DocBrowser — a VERDADE VISUAL da casa, como doc completa. Seção multi-página: conceitos
|
|
3
|
+
* (Introdução, Tokens & Tema, Microcopy, Customização) + uma página por componente do
|
|
4
|
+
* opus/ui, agrupados por seção. O que você vê aqui é o que os apps entregam (o site
|
|
5
|
+
* do shadcn é catálogo do que existe; a expectativa visual nasce aqui).
|
|
6
|
+
*
|
|
7
|
+
* Dois modos de roteamento:
|
|
8
|
+
* - CONTROLADO (`path` vem de fora): o host (ex.: Maestro) é dono do path; o DocBrowser
|
|
9
|
+
* só lê e navega.
|
|
10
|
+
* - STANDALONE (`path` ausente): o path vem do router da casa — é o modo da rota dev
|
|
11
|
+
* `/__docs` de cada app (a doc com a identidade do projeto).
|
|
12
|
+
* Nos dois, navegar emite `popstate`: host que guarda o path por conta própria acompanha.
|
|
13
|
+
* `basePath` adapta o prefixo das URLs (`/docs` no Maestro, `/__docs` nos apps).
|
|
14
|
+
*/
|
|
15
|
+
import { useEffect } from 'react'
|
|
16
|
+
import { DOC_SECTIONS, type DocSection, type DocEntry } from './registry'
|
|
17
|
+
import { SectionShell, type SectionNavGroup } from '../components/patterns/section-shell.tsx'
|
|
18
|
+
import { navigate, usePathname } from '../router.ts'
|
|
19
|
+
|
|
20
|
+
function findPage(sections: DocSection[], slug: string): DocEntry | undefined {
|
|
21
|
+
for (const s of sections) {
|
|
22
|
+
for (const g of s.groups) {
|
|
23
|
+
const p = g.pages.find((p) => p.slug === slug)
|
|
24
|
+
if (p) return p
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface DocBrowserProps {
|
|
31
|
+
/** Prefixo das rotas da doc. Default `/docs` (Maestro); `/__docs` na rota dev dos apps. */
|
|
32
|
+
basePath?: string
|
|
33
|
+
/** Path controlado pelo host (modo Maestro). Ausente = standalone (gerencia o próprio). */
|
|
34
|
+
path?: string
|
|
35
|
+
/** Seções a exibir (seção → grupo → página). Default = catálogo inteiro. */
|
|
36
|
+
sections?: DocSection[]
|
|
37
|
+
/** Monta o `document.title` a partir do título da página atual (ex.: `(t) => `${t} · Opus``).
|
|
38
|
+
* Ausente = não mexe no title. */
|
|
39
|
+
documentTitle?: (pageTitle: string) => string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function DocBrowser({
|
|
43
|
+
basePath = '/docs',
|
|
44
|
+
path: controlledPath,
|
|
45
|
+
sections = DOC_SECTIONS,
|
|
46
|
+
documentTitle,
|
|
47
|
+
}: DocBrowserProps): React.ReactElement {
|
|
48
|
+
// Standalone: o path vem do router da casa (`usePathname` — pathname É o estado).
|
|
49
|
+
// Controlado: o host manda, e este valor é ignorado. O hook roda sempre (regra dos
|
|
50
|
+
// hooks); o que muda é quem vence no `??`.
|
|
51
|
+
const livePath = usePathname()
|
|
52
|
+
const path = controlledPath ?? livePath
|
|
53
|
+
// Default = 1ª página do 1º grupo da 1ª seção (a doc abre na 1ª página).
|
|
54
|
+
const defaultSlug = sections[0]?.groups[0]?.pages[0]?.slug ?? ''
|
|
55
|
+
// Slug = TODOS os segmentos após o basePath, unidos (rota aninhada: `/docs/guias/deploy`
|
|
56
|
+
// → `guias/deploy`). Slug flat de 1 segmento continua igual (`join` de 1 elemento = ele).
|
|
57
|
+
const rel = path.startsWith(basePath) ? path.slice(basePath.length) : ''
|
|
58
|
+
const slug = rel.split('/').filter(Boolean).join('/') || defaultSlug
|
|
59
|
+
const page = findPage(sections, slug) ?? findPage(sections, defaultSlug)
|
|
60
|
+
|
|
61
|
+
// `navigate` emite `popstate`, então o host é notificado nos dois modos — inclusive o
|
|
62
|
+
// que ainda escuta o evento na unha, que era como este componente avisava antes.
|
|
63
|
+
const go = (to: string): void => navigate(to)
|
|
64
|
+
|
|
65
|
+
// O title da aba acompanha a página atual (quando o host fornece o formato).
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (page && documentTitle) document.title = documentTitle(page.title)
|
|
68
|
+
}, [page, documentTitle])
|
|
69
|
+
|
|
70
|
+
// Seção → grupo, e os grupos da seção → subgrupos. O tier do meio NÃO pode ser
|
|
71
|
+
// achatado: `docSectionsFromFolder` o preenche a partir de sub-pasta (ou do
|
|
72
|
+
// frontmatter `group:`), que é o caminho do `opusDocs({ source })`.
|
|
73
|
+
const navGroups: SectionNavGroup[] = sections.map((section) => ({
|
|
74
|
+
label: section.label,
|
|
75
|
+
subgroups: section.groups.map((g) => ({
|
|
76
|
+
label: g.label,
|
|
77
|
+
items: g.pages.map((p) => ({ id: p.slug, label: p.title, badge: p.badge })),
|
|
78
|
+
})),
|
|
79
|
+
}))
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<SectionShell
|
|
83
|
+
groups={navGroups}
|
|
84
|
+
activeId={page?.slug}
|
|
85
|
+
onSelect={(slug) => go(`${basePath}/${slug}`)}
|
|
86
|
+
>
|
|
87
|
+
<div className="mx-auto max-w-3xl px-8 py-8">{page?.render()}</div>
|
|
88
|
+
</SectionShell>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChangelogView — apresentação do CHANGELOG.md do pacote (a MESMA fonte que viaja em
|
|
3
|
+
* node_modules): linha do tempo por versão, data ao lado, e a seção Breaking em
|
|
4
|
+
* destaque (badge + contêiner) — o leitor que pula versões acha as quebras no olho.
|
|
5
|
+
* O parser é convenção-fina sobre o markdown (`## <versão> — <data>` + `### Breaking`);
|
|
6
|
+
* o corpo de cada trecho segue renderizado pelo DocMarkdown (fidelidade total).
|
|
7
|
+
*/
|
|
8
|
+
import { Badge } from '../react.tsx'
|
|
9
|
+
import { DocMarkdown } from './markdown.tsx'
|
|
10
|
+
// A fonte é o CHANGELOG DO PACOTE (raiz) — a mesma que viaja em node_modules.
|
|
11
|
+
import changelogMd from '../../../CHANGELOG.md?raw'
|
|
12
|
+
|
|
13
|
+
export interface ChangelogVersion {
|
|
14
|
+
version: string
|
|
15
|
+
date?: string
|
|
16
|
+
body: string
|
|
17
|
+
breaking?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ParsedChangelog {
|
|
21
|
+
intro: string
|
|
22
|
+
versions: ChangelogVersion[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Parseia o changelog: intro (antes do 1º `## `) + versões (`## x — data`, `### Breaking`). */
|
|
26
|
+
export function parseChangelog(source: string): ParsedChangelog {
|
|
27
|
+
const chunks = `\n${source.trim()}`.split(/\n## /)
|
|
28
|
+
const intro = chunks[0]?.trim() ?? ''
|
|
29
|
+
const versions: ChangelogVersion[] = []
|
|
30
|
+
for (const chunk of chunks.slice(1)) {
|
|
31
|
+
const nl = chunk.indexOf('\n')
|
|
32
|
+
const header = (nl === -1 ? chunk : chunk.slice(0, nl)).trim()
|
|
33
|
+
const rest = nl === -1 ? '' : chunk.slice(nl + 1)
|
|
34
|
+
const [version, date] = header.split(/\s+—\s+/)
|
|
35
|
+
const [body, breaking] = rest.split(/\n### Breaking\s*\n/)
|
|
36
|
+
versions.push({
|
|
37
|
+
version: (version ?? header).trim(),
|
|
38
|
+
...(date !== undefined ? { date: date.trim() } : {}),
|
|
39
|
+
body: (body ?? '').trim(),
|
|
40
|
+
...(breaking !== undefined ? { breaking: breaking.trim() } : {}),
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
return { intro, versions }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function ChangelogView({ source }: { source: string }): React.ReactElement {
|
|
47
|
+
const { intro, versions } = parseChangelog(source)
|
|
48
|
+
return (
|
|
49
|
+
<div>
|
|
50
|
+
{intro.length > 0 && <DocMarkdown content={intro} />}
|
|
51
|
+
<div className="mt-10 space-y-10">
|
|
52
|
+
{versions.map((v) => (
|
|
53
|
+
<section key={v.version} className="border-t border-border/60 pt-6">
|
|
54
|
+
<div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
|
|
55
|
+
<h2 className="text-xl font-semibold tracking-tight">{v.version}</h2>
|
|
56
|
+
{v.date !== undefined && <span className="text-sm text-muted-foreground">{v.date}</span>}
|
|
57
|
+
{v.breaking !== undefined && <Badge variant="destructive">Breaking</Badge>}
|
|
58
|
+
</div>
|
|
59
|
+
{v.body.length > 0 && (
|
|
60
|
+
<div className="mt-3">
|
|
61
|
+
<DocMarkdown content={v.body} />
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
{v.breaking !== undefined && (
|
|
65
|
+
<div className="mt-4 rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3">
|
|
66
|
+
<div className="mb-2 text-xs font-medium text-destructive">Breaking — o que fazer</div>
|
|
67
|
+
<DocMarkdown content={v.breaking} />
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</section>
|
|
71
|
+
))}
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** O changelog do Opus pronto pra montar (site: página de topo; Maestro: página da doc). */
|
|
78
|
+
export function OpusChangelog(): React.ReactElement {
|
|
79
|
+
return <ChangelogView source={changelogMd} />
|
|
80
|
+
}
|