@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,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opusDocs() — plugin vite que serve a doc do DocBrowser numa rota DEV (`/__docs`) do app.
|
|
3
|
+
*
|
|
4
|
+
* Por quê: o Maestro mostra a "verdade da base" em /docs; cada app expõe a MESMA doc
|
|
5
|
+
* dentro do próprio dev server → veste o tema do projeto e a versão do Opus pinada ali
|
|
6
|
+
* (a "verdade por-projeto"). Como o preview da sessão proxeia por Host pro dev server
|
|
7
|
+
* do worktree, abrir /__docs no preview mostra a doc com a identidade daquele projeto.
|
|
8
|
+
*
|
|
9
|
+
* Como: um módulo virtual importa o CSS do app (default /src/index.css — onde mora o
|
|
10
|
+
* import do theme.css + as sobrescritas de token + o @source do tailwind) e então chama
|
|
11
|
+
* `mountDocs`. Importando o MESMO CSS do app, a doc herda identidade e utilitários de
|
|
12
|
+
* graça. É dev-only (apply: 'serve') — nada vai pro bundle de produção.
|
|
13
|
+
*
|
|
14
|
+
* Uso (vite.config.ts do app):
|
|
15
|
+
* import { opusDocs } from '@softize/opus/ui/docs/plugin'
|
|
16
|
+
* export default defineConfig({ plugins: [react(), tailwindcss(), opusDocs()] })
|
|
17
|
+
*
|
|
18
|
+
* Requer no app: react-dom e @tanstack/react-query (as páginas de pattern exercitam o
|
|
19
|
+
* driver do Opus) — peers que os apps web do Opus já têm. Sem react-query, a doc não sobe.
|
|
20
|
+
*/
|
|
21
|
+
import type { Plugin } from 'vite'
|
|
22
|
+
|
|
23
|
+
export interface opusDocsOptions {
|
|
24
|
+
/** Prefixo da rota. Default '/__docs'. */
|
|
25
|
+
basePath?: string
|
|
26
|
+
/** Entry CSS do app (a doc herda tema + tailwind dele). Default '/src/index.css'. */
|
|
27
|
+
css?: string
|
|
28
|
+
/** Pasta de markdown DO PROJETO a servir (ex.: 'docs'). Ausente = a doc do próprio Opus
|
|
29
|
+
* (DOC_SECTIONS). Com `source`, varre os `.md` da pasta e monta a nav espelhando a árvore. */
|
|
30
|
+
source?: string
|
|
31
|
+
/** Título no topo (só quando `source`). Default 'Docs'. */
|
|
32
|
+
title?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const VIRTUAL_ID = 'virtual:opus-docs-entry'
|
|
36
|
+
// O `/@id/` é como o vite serve módulos por id; o `\0` (convenção de virtual) vira `__x00__` na URL.
|
|
37
|
+
const RESOLVED_ID = '\0' + VIRTUAL_ID
|
|
38
|
+
const ENTRY_URL = '/@id/__x00__' + VIRTUAL_ID
|
|
39
|
+
|
|
40
|
+
export function opusDocs(options: opusDocsOptions = {}): Plugin {
|
|
41
|
+
const basePath = options.basePath ?? '/__docs'
|
|
42
|
+
const css = options.css ?? '/src/index.css'
|
|
43
|
+
const source = options.source?.replace(/^\/+|\/+$/g, '')
|
|
44
|
+
const title = options.title ?? 'Docs'
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
name: 'opus-docs',
|
|
48
|
+
apply: 'serve',
|
|
49
|
+
resolveId(id) {
|
|
50
|
+
if (id === VIRTUAL_ID) return RESOLVED_ID
|
|
51
|
+
return undefined
|
|
52
|
+
},
|
|
53
|
+
load(id) {
|
|
54
|
+
if (id !== RESOLVED_ID) return undefined
|
|
55
|
+
// Sempre importa o CSS do app (tema + tailwind) antes de subir a doc.
|
|
56
|
+
const head = `import ${JSON.stringify(css)}`
|
|
57
|
+
if (source === undefined || source === '') {
|
|
58
|
+
// Sem `source`: a doc do próprio Opus (DOC_SECTIONS), como sempre.
|
|
59
|
+
return [head, `import { mountDocs } from '@softize/opus/ui/docs/standalone'`, `mountDocs({ basePath: ${JSON.stringify(basePath)} })`].join('\n')
|
|
60
|
+
}
|
|
61
|
+
// Com `source`: varre a pasta `.md` do projeto (glob eager) → DocSection[].
|
|
62
|
+
// O glob roda no contexto do app (a chave é `/<source>/…`); o transform mora no Opus.
|
|
63
|
+
return [
|
|
64
|
+
head,
|
|
65
|
+
`import { mountDocs, docSectionsFromFolder } from '@softize/opus/ui/docs/standalone'`,
|
|
66
|
+
`const files = import.meta.glob(${JSON.stringify('/' + source + '/**/*.md')}, { query: '?raw', import: 'default', eager: true })`,
|
|
67
|
+
`mountDocs({ basePath: ${JSON.stringify(basePath)}, title: ${JSON.stringify(title)}, sections: docSectionsFromFolder(files, { root: ${JSON.stringify(source)} }) })`,
|
|
68
|
+
].join('\n')
|
|
69
|
+
},
|
|
70
|
+
configureServer(server) {
|
|
71
|
+
server.middlewares.use((req, res, next) => {
|
|
72
|
+
const url = (req.url ?? '').split('?')[0] ?? ''
|
|
73
|
+
const onDocBrowser = url === basePath || url.startsWith(basePath + '/')
|
|
74
|
+
if (!onDocBrowser) {
|
|
75
|
+
next()
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
const html = [
|
|
79
|
+
'<!doctype html>',
|
|
80
|
+
'<html lang="pt-BR">',
|
|
81
|
+
' <head>',
|
|
82
|
+
' <meta charset="UTF-8" />',
|
|
83
|
+
' <meta name="viewport" content="width=device-width, initial-scale=1.0" />',
|
|
84
|
+
' <title>Components</title>',
|
|
85
|
+
' </head>',
|
|
86
|
+
' <body>',
|
|
87
|
+
' <div id="root"></div>',
|
|
88
|
+
` <script type="module" src=${JSON.stringify(ENTRY_URL)}></script>`,
|
|
89
|
+
' </body>',
|
|
90
|
+
'</html>',
|
|
91
|
+
].join('\n')
|
|
92
|
+
// transformIndexHtml injeta o client de HMR + roda os outros plugins de html.
|
|
93
|
+
server
|
|
94
|
+
.transformIndexHtml(req.originalUrl ?? url, html)
|
|
95
|
+
.then((out) => {
|
|
96
|
+
res.statusCode = 200
|
|
97
|
+
res.setHeader('Content-Type', 'text/html')
|
|
98
|
+
res.end(out)
|
|
99
|
+
})
|
|
100
|
+
.catch(next)
|
|
101
|
+
})
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* registry.tsx — o mapa da doc: seção → grupo (opcional) → página. Toda página nova
|
|
3
|
+
* entra AQUI (e só aqui) — o shell deriva o resto. O site tem DUAS superfícies de
|
|
4
|
+
* NAVEGAÇÃO do MESMO SDK (a UI faz parte dele): `PROTOCOL_SECTIONS` (/docs — o protocolo,
|
|
5
|
+
* recursos, ciclo de vida) e `UI_SECTIONS` (/ui — o catálogo de componentes, design,
|
|
6
|
+
* patterns); `DOC_SECTIONS` é a união (Maestro e /__docs mostram tudo numa nav só).
|
|
7
|
+
*
|
|
8
|
+
* O CORPO de cada página vive em markdown (`./content/*.md`, importado como `?raw`) e é
|
|
9
|
+
* renderizado pelo DocMarkdown — inclusive os exemplos vivos (```tsx preview). Páginas de
|
|
10
|
+
* componente ganham o cabeçalho (título + badge de origem + whenToUse) do `meta` central via o
|
|
11
|
+
* helper `comp`; páginas conceituais renderizam o markdown direto (o próprio md traz o H1/lead).
|
|
12
|
+
*/
|
|
13
|
+
import { useEffect } from 'react'
|
|
14
|
+
import * as lucideIcons from 'lucide-react'
|
|
15
|
+
import * as opusUi from '../react.tsx'
|
|
16
|
+
import { componentMeta } from '../meta.ts'
|
|
17
|
+
import { navigate } from '../router.ts'
|
|
18
|
+
import { DocPage } from './doc.tsx'
|
|
19
|
+
import { DocMarkdown } from './markdown.tsx'
|
|
20
|
+
import { OpusChangelog } from './changelog.tsx'
|
|
21
|
+
|
|
22
|
+
// — Conceituais (Começar/Framework/Design/Introdução) —
|
|
23
|
+
import gettingStartedMd from './content/getting-started.md?raw'
|
|
24
|
+
import upgradingMd from './content/upgrading.md?raw'
|
|
25
|
+
import cycleMd from './content/cycle.md?raw'
|
|
26
|
+
import authMd from './content/auth.md?raw'
|
|
27
|
+
import auditMd from './content/audit.md?raw'
|
|
28
|
+
import eventsMd from './content/events.md?raw'
|
|
29
|
+
import logMd from './content/log.md?raw'
|
|
30
|
+
import queueMd from './content/queue.md?raw'
|
|
31
|
+
import schedulerMd from './content/scheduler.md?raw'
|
|
32
|
+
import actionsMd from './content/actions.md?raw'
|
|
33
|
+
import dataMd from './content/data.md?raw'
|
|
34
|
+
import storageMd from './content/storage.md?raw'
|
|
35
|
+
import aiMd from './content/ai.md?raw'
|
|
36
|
+
import mcpMd from './content/mcp.md?raw'
|
|
37
|
+
import testingMd from './content/testing.md?raw'
|
|
38
|
+
import runtimeMd from './content/runtime.md?raw'
|
|
39
|
+
import uiMd from './content/ui.md?raw'
|
|
40
|
+
import cliMd from './content/cli.md?raw'
|
|
41
|
+
import tokensMd from './content/tokens.md?raw'
|
|
42
|
+
import microcopyMd from './content/microcopy.md?raw'
|
|
43
|
+
import customizationMd from './content/customization.md?raw'
|
|
44
|
+
|
|
45
|
+
// — Componentes —
|
|
46
|
+
import accordionMd from './content/accordion.md?raw'
|
|
47
|
+
import alertMd from './content/alert.md?raw'
|
|
48
|
+
import alertDialogMd from './content/alert-dialog.md?raw'
|
|
49
|
+
import confirmMd from './content/confirm.md?raw'
|
|
50
|
+
import aspectRatioMd from './content/aspect-ratio.md?raw'
|
|
51
|
+
import avatarMd from './content/avatar.md?raw'
|
|
52
|
+
import badgeMd from './content/badge.md?raw'
|
|
53
|
+
import breadcrumbMd from './content/breadcrumb.md?raw'
|
|
54
|
+
import buttonMd from './content/button.md?raw'
|
|
55
|
+
import buttonGroupMd from './content/button-group.md?raw'
|
|
56
|
+
import calendarMd from './content/calendar.md?raw'
|
|
57
|
+
import cardMd from './content/card.md?raw'
|
|
58
|
+
import carouselMd from './content/carousel.md?raw'
|
|
59
|
+
import chatMd from './content/chat.md?raw'
|
|
60
|
+
import checkboxMd from './content/checkbox.md?raw'
|
|
61
|
+
import collapsibleMd from './content/collapsible.md?raw'
|
|
62
|
+
import iconPickerMd from './content/icon-picker.md?raw'
|
|
63
|
+
import commandMd from './content/command.md?raw'
|
|
64
|
+
import composerMd from './content/composer.md?raw'
|
|
65
|
+
import copyableMd from './content/copyable.md?raw'
|
|
66
|
+
import dialogMd from './content/dialog.md?raw'
|
|
67
|
+
import drawerMd from './content/drawer.md?raw'
|
|
68
|
+
import menuMd from './content/menu.md?raw'
|
|
69
|
+
import emptyMd from './content/empty.md?raw'
|
|
70
|
+
import fieldMd from './content/field.md?raw'
|
|
71
|
+
import inputMd from './content/input.md?raw'
|
|
72
|
+
import inputGroupMd from './content/input-group.md?raw'
|
|
73
|
+
import inputOtpMd from './content/input-otp.md?raw'
|
|
74
|
+
import itemMd from './content/item.md?raw'
|
|
75
|
+
import kbdMd from './content/kbd.md?raw'
|
|
76
|
+
import labelMd from './content/label.md?raw'
|
|
77
|
+
import markdownMd from './content/markdown.md?raw'
|
|
78
|
+
import paginationMd from './content/pagination.md?raw'
|
|
79
|
+
import popoverMd from './content/popover.md?raw'
|
|
80
|
+
import progressMd from './content/progress.md?raw'
|
|
81
|
+
import radioGroupMd from './content/radio-group.md?raw'
|
|
82
|
+
import resizableMd from './content/resizable.md?raw'
|
|
83
|
+
import scrollAreaMd from './content/scroll-area.md?raw'
|
|
84
|
+
import selectMd from './content/select.md?raw'
|
|
85
|
+
import separatorMd from './content/separator.md?raw'
|
|
86
|
+
import skeletonMd from './content/skeleton.md?raw'
|
|
87
|
+
import sliderMd from './content/slider.md?raw'
|
|
88
|
+
import spinnerMd from './content/spinner.md?raw'
|
|
89
|
+
import switchMd from './content/switch.md?raw'
|
|
90
|
+
import tableMd from './content/table.md?raw'
|
|
91
|
+
import tabsMd from './content/tabs.md?raw'
|
|
92
|
+
import textareaMd from './content/textarea.md?raw'
|
|
93
|
+
import toastMd from './content/toast.md?raw'
|
|
94
|
+
import toggleMd from './content/toggle.md?raw'
|
|
95
|
+
import toggleGroupMd from './content/toggle-group.md?raw'
|
|
96
|
+
import tooltipMd from './content/tooltip.md?raw'
|
|
97
|
+
import truncateMd from './content/truncate.md?raw'
|
|
98
|
+
|
|
99
|
+
// — Patterns (markdown + palco doc-client injetado no scope dos previews) —
|
|
100
|
+
import {
|
|
101
|
+
DocBrowserActionProvider,
|
|
102
|
+
docSessionArchive,
|
|
103
|
+
docSessionDelete,
|
|
104
|
+
docWorkspaceCreate,
|
|
105
|
+
docWorkspaceList,
|
|
106
|
+
docWorkspaceView,
|
|
107
|
+
} from './doc-client'
|
|
108
|
+
import actionFormMd from './content/action-form.md?raw'
|
|
109
|
+
import actionFormDialogMd from './content/action-form-dialog.md?raw'
|
|
110
|
+
import actionFormCardMd from './content/action-form-card.md?raw'
|
|
111
|
+
import actionSearchMd from './content/action-list.md?raw'
|
|
112
|
+
import actionViewMd from './content/action-view.md?raw'
|
|
113
|
+
import actionTriggerMd from './content/action-trigger.md?raw'
|
|
114
|
+
import actionSearchDialogMd from './content/action-list-dialog.md?raw'
|
|
115
|
+
import pageMd from './content/page.md?raw'
|
|
116
|
+
import appShellMd from './content/app-shell.md?raw'
|
|
117
|
+
import sectionShellMd from './content/section-shell.md?raw'
|
|
118
|
+
import sidebarMd from './content/sidebar.md?raw'
|
|
119
|
+
import splitMd from './content/split.md?raw'
|
|
120
|
+
import routerMd from './content/router.md?raw'
|
|
121
|
+
import dataStateMd from './content/data-state.md?raw'
|
|
122
|
+
|
|
123
|
+
export interface DocEntry {
|
|
124
|
+
slug: string
|
|
125
|
+
title: string
|
|
126
|
+
/** Tag técnica opcional exibida na nav ao lado do título — o subpath do SDK (ex.: 'auth'). */
|
|
127
|
+
badge?: string
|
|
128
|
+
render: () => React.ReactElement
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface DocGroup {
|
|
132
|
+
/** Sub-rótulo opcional dentro da seção (ex.: Primitivos). Ausente = páginas direto na seção. */
|
|
133
|
+
label?: string
|
|
134
|
+
pages: DocEntry[]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface DocSection {
|
|
138
|
+
label: string
|
|
139
|
+
groups: DocGroup[]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* O scope de previews da doc do Opus: a paleta de ícones (lucide inteiro) que os exemplos citam
|
|
144
|
+
* por nome. Fica AQUI (no catálogo), não no DocMarkdown — assim só a superfície que exibe estes
|
|
145
|
+
* docs empacota o lucide; um consumidor que só usa o renderer (docs do próprio projeto) fica leve.
|
|
146
|
+
*/
|
|
147
|
+
// Ícone homônimo de componente (Badge, Table, Calendar, Command…) fica FORA do scope:
|
|
148
|
+
// o scope injetado entra POR CIMA do default no DocMarkdown, e `<Badge>` num exemplo
|
|
149
|
+
// tem que ser o COMPONENTE (chip), não o selo do lucide. Ícone colidido, se um exemplo
|
|
150
|
+
// precisar, entra por alias no scope do chamador.
|
|
151
|
+
const iconScope: Record<string, unknown> = Object.fromEntries(
|
|
152
|
+
Object.entries(lucideIcons).filter(([name]) => !(name in opusUi)),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
/** Página conceitual (Começar/Framework/Design/Intro): markdown direto (o H1/lead moram no md). */
|
|
156
|
+
const doc = (content: string) => (): React.ReactElement => <DocMarkdown content={content} scope={iconScope} />
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Página de componente: o cabeçalho (título + badge de origem + whenToUse) vem do `meta` central
|
|
160
|
+
* (fonte única — manifesto e ferramentas leem o mesmo), o corpo (exemplos vivos + props) vem do
|
|
161
|
+
* markdown. Fiel ao DocPage original — só o corpo deixou de ser JSX e virou `.md`.
|
|
162
|
+
*/
|
|
163
|
+
const comp =
|
|
164
|
+
(title: string, key: keyof typeof componentMeta, content: string) =>
|
|
165
|
+
(): React.ReactElement => (
|
|
166
|
+
<DocPage title={title} meta={componentMeta[key]}>
|
|
167
|
+
<DocMarkdown content={content} scope={iconScope} />
|
|
168
|
+
</DocPage>
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
/** O palco dos patterns no scope dos previews: ícones + client fake + contratos de mentira. */
|
|
172
|
+
const patternScope = {
|
|
173
|
+
...iconScope,
|
|
174
|
+
DocBrowserActionProvider,
|
|
175
|
+
docWorkspaceCreate,
|
|
176
|
+
docWorkspaceList,
|
|
177
|
+
docWorkspaceView,
|
|
178
|
+
docSessionArchive,
|
|
179
|
+
docSessionDelete,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Página de pattern: igual ao `comp`, mas os previews enxergam o palco doc-client no scope. */
|
|
183
|
+
const pattern =
|
|
184
|
+
(title: string, key: keyof typeof componentMeta, content: string) =>
|
|
185
|
+
(): React.ReactElement => (
|
|
186
|
+
<DocPage title={title} meta={componentMeta[key]}>
|
|
187
|
+
<DocMarkdown content={content} scope={patternScope} />
|
|
188
|
+
</DocPage>
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
/** Entrada 'UI' do Docs: a UI é um recurso do MESMO SDK, mas mora na superfície `/ui` (o
|
|
192
|
+
* catálogo é grande e visual). Em vez de duplicar, a página redireciona pra lá. */
|
|
193
|
+
function UiRedirect(): React.ReactElement {
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
if (typeof window === 'undefined') return
|
|
196
|
+
// `replace`: um redirect não merece uma entrada no histórico — com `push`, o "voltar"
|
|
197
|
+
// cairia aqui de novo e redirecionaria outra vez.
|
|
198
|
+
navigate('/ui', { replace: true })
|
|
199
|
+
}, [])
|
|
200
|
+
return <p className="text-sm text-muted-foreground">Redirecionando para a UI…</p>
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** As seções da doc do SDK (surface `/docs` do site). "Começar" fica na RAIZ (seção sem
|
|
204
|
+
* label); depois o Protocolo (núcleo invariante) e os Recursos (peças opt-in). */
|
|
205
|
+
export const PROTOCOL_SECTIONS: DocSection[] = [
|
|
206
|
+
{
|
|
207
|
+
// Sem label → páginas soltas no topo do menu (sem cabeçalho de seção).
|
|
208
|
+
label: '',
|
|
209
|
+
groups: [
|
|
210
|
+
{
|
|
211
|
+
pages: [
|
|
212
|
+
{ slug: 'getting-started', title: 'Getting started', render: doc(gettingStartedMd) },
|
|
213
|
+
{ slug: 'cycle', title: 'Evolução contínua', render: doc(cycleMd) },
|
|
214
|
+
{ slug: 'upgrading', title: 'Atualizar a base', render: doc(upgradingMd) },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
// O núcleo invariante do protocolo — uma página por recurso. Opus é protocolo + peças
|
|
221
|
+
// opcionais entregues como SDK (não Framework: a inversão de controle é do app).
|
|
222
|
+
label: 'Protocolo',
|
|
223
|
+
groups: [
|
|
224
|
+
{
|
|
225
|
+
pages: [
|
|
226
|
+
{ slug: 'actions', title: 'Actions & contratos', render: doc(actionsMd) },
|
|
227
|
+
{ slug: 'runtime', title: 'Runtime & adapters', render: doc(runtimeMd) },
|
|
228
|
+
{ slug: 'data', title: 'Camada de dados', render: doc(dataMd) },
|
|
229
|
+
{ slug: 'testing', title: 'Testes de action', render: doc(testingMd) },
|
|
230
|
+
{ slug: 'cli', title: 'CLI opus', render: doc(cliMd) },
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
// As peças OPT-IN que você liga (adapter + driver). A UI também é recurso do SDK, mas
|
|
237
|
+
// com catálogo próprio — a entrada 'ui' redireciona pra superfície /ui.
|
|
238
|
+
label: 'Recursos',
|
|
239
|
+
groups: [
|
|
240
|
+
{
|
|
241
|
+
pages: [
|
|
242
|
+
{ slug: 'auth', title: 'Autenticação', badge: 'auth', render: doc(authMd) },
|
|
243
|
+
{ slug: 'audit', title: 'Auditoria', badge: 'audit', render: doc(auditMd) },
|
|
244
|
+
{ slug: 'events', title: 'Eventos', badge: 'events', render: doc(eventsMd) },
|
|
245
|
+
{ slug: 'log', title: 'Log', badge: 'log', render: doc(logMd) },
|
|
246
|
+
{ slug: 'queue', title: 'Filas', badge: 'queue', render: doc(queueMd) },
|
|
247
|
+
{ slug: 'scheduler', title: 'Agendador', badge: 'scheduler', render: doc(schedulerMd) },
|
|
248
|
+
{ slug: 'storage', title: 'Armazenamento', badge: 'storage', render: doc(storageMd) },
|
|
249
|
+
{ slug: 'ai', title: 'IA generativa', badge: 'ai', render: doc(aiMd) },
|
|
250
|
+
{ slug: 'mcp', title: 'MCP', badge: 'mcp', render: doc(mcpMd) },
|
|
251
|
+
{ slug: 'ui', title: 'UI ↗', render: () => <UiRedirect /> },
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
]
|
|
257
|
+
|
|
258
|
+
/** As seções da parte de UI do SDK (`/ui`): consumo, design, catálogo de componentes e
|
|
259
|
+
* patterns. Mesma família das seções de protocolo — superfície própria só pelo volume. */
|
|
260
|
+
export const UI_SECTIONS: DocSection[] = [
|
|
261
|
+
{
|
|
262
|
+
// Raiz (sem label): o "getting started" da UI.
|
|
263
|
+
label: '',
|
|
264
|
+
groups: [{ pages: [{ slug: 'como-consumir', title: 'Getting started', render: doc(uiMd) }] }],
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
label: 'Design',
|
|
268
|
+
groups: [
|
|
269
|
+
{
|
|
270
|
+
pages: [
|
|
271
|
+
{ slug: 'tokens', title: 'Tokens & Tema', render: doc(tokensMd) },
|
|
272
|
+
{ slug: 'microcopy', title: 'Microcopy', render: doc(microcopyMd) },
|
|
273
|
+
{ slug: 'customization', title: 'Customização', render: doc(customizationMd) },
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
label: 'Componentes',
|
|
280
|
+
groups: [
|
|
281
|
+
{
|
|
282
|
+
label: 'Fundamentos',
|
|
283
|
+
pages: [
|
|
284
|
+
{ slug: 'router', title: 'Router', render: pattern('Router', 'router', routerMd) },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
label: 'Layout',
|
|
289
|
+
pages: [
|
|
290
|
+
{ slug: 'split', title: 'Split & Pane', render: pattern('Split & Pane', 'split', splitMd) },
|
|
291
|
+
{ slug: 'sidebar', title: 'Sidebar', render: pattern('Sidebar', 'sidebar', sidebarMd) },
|
|
292
|
+
{ slug: 'page', title: 'Page', render: pattern('Page', 'page', pageMd) },
|
|
293
|
+
{ slug: 'accordion', title: 'Accordion', render: comp('Accordion', 'accordion', accordionMd) },
|
|
294
|
+
{ slug: 'aspect-ratio', title: 'Aspect Ratio', render: comp('Aspect Ratio', 'aspect-ratio', aspectRatioMd) },
|
|
295
|
+
{ slug: 'card', title: 'Card', render: comp('Card', 'card', cardMd) },
|
|
296
|
+
{ slug: 'collapsible', title: 'Collapsible', render: comp('Collapsible', 'collapsible', collapsibleMd) },
|
|
297
|
+
{ slug: 'resizable', title: 'Resizable', badge: componentMeta.resizable.deprecated ? 'deprecated' : undefined, render: comp('Resizable', 'resizable', resizableMd) },
|
|
298
|
+
{ slug: 'scroll-area', title: 'Scroll Area', render: comp('Scroll Area', 'scroll-area', scrollAreaMd) },
|
|
299
|
+
{ slug: 'separator', title: 'Separator', render: comp('Separator', 'separator', separatorMd) },
|
|
300
|
+
{ slug: 'app-shell', title: 'App Shell', badge: componentMeta['app-shell'].deprecated ? 'deprecated' : undefined, render: pattern('App Shell', 'app-shell', appShellMd) },
|
|
301
|
+
{ slug: 'section-shell', title: 'Section Shell', badge: componentMeta['section-shell'].deprecated ? 'deprecated' : undefined, render: pattern('Section Shell', 'section-shell', sectionShellMd) },
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
label: 'Actions',
|
|
306
|
+
pages: [
|
|
307
|
+
{ slug: 'action-form', title: 'Action Form', render: pattern('Action Form', 'action-form', actionFormMd) },
|
|
308
|
+
{ slug: 'action-form-card', title: 'Action Form Card', render: pattern('Action Form Card', 'action-form-card', actionFormCardMd) },
|
|
309
|
+
{ slug: 'action-form-dialog', title: 'Action Form Dialog', render: pattern('Action Form Dialog', 'action-form-dialog', actionFormDialogMd) },
|
|
310
|
+
{ slug: 'action-list', title: 'Action List', render: pattern('Action List', 'action-list', actionSearchMd) },
|
|
311
|
+
{ slug: 'action-list-dialog', title: 'Action List Dialog', render: pattern('Action List Dialog', 'action-list-dialog', actionSearchDialogMd) },
|
|
312
|
+
{ slug: 'action-trigger', title: 'Action Trigger', render: pattern('Action Trigger', 'action-trigger', actionTriggerMd) },
|
|
313
|
+
{ slug: 'action-view', title: 'Action View', render: pattern('Action View', 'action-view', actionViewMd) },
|
|
314
|
+
],
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
label: 'IA',
|
|
318
|
+
pages: [
|
|
319
|
+
{ slug: 'chat', title: 'Chat', render: comp('Chat', 'chat', chatMd) },
|
|
320
|
+
{ slug: 'composer', title: 'Composer', render: comp('Composer', 'composer', composerMd) },
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
label: 'Formulário',
|
|
325
|
+
pages: [
|
|
326
|
+
{ slug: 'calendar', title: 'Calendar', render: comp('Calendar', 'calendar', calendarMd) },
|
|
327
|
+
{ slug: 'checkbox', title: 'Checkbox', render: comp('Checkbox', 'checkbox', checkboxMd) },
|
|
328
|
+
{ slug: 'field', title: 'Field', render: comp('Field', 'field', fieldMd) },
|
|
329
|
+
{ slug: 'icon-picker', title: 'Icon Picker', render: comp('Icon Picker', 'icon-picker', iconPickerMd) },
|
|
330
|
+
{ slug: 'input', title: 'Input', render: comp('Input', 'input', inputMd) },
|
|
331
|
+
{ slug: 'input-group', title: 'Input Group', render: comp('Input Group', 'input-group', inputGroupMd) },
|
|
332
|
+
{ slug: 'input-otp', title: 'Input OTP', render: comp('Input OTP', 'input-otp', inputOtpMd) },
|
|
333
|
+
{ slug: 'label', title: 'Label', render: comp('Label', 'label', labelMd) },
|
|
334
|
+
{ slug: 'radio-group', title: 'Radio Group', render: comp('Radio Group', 'radio-group', radioGroupMd) },
|
|
335
|
+
{ slug: 'select', title: 'Select', render: comp('Select', 'select', selectMd) },
|
|
336
|
+
{ slug: 'slider', title: 'Slider', render: comp('Slider', 'slider', sliderMd) },
|
|
337
|
+
{ slug: 'switch', title: 'Switch', render: comp('Switch', 'switch', switchMd) },
|
|
338
|
+
{ slug: 'textarea', title: 'Textarea', render: comp('Textarea', 'textarea', textareaMd) },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
label: 'Botões',
|
|
343
|
+
pages: [
|
|
344
|
+
{ slug: 'button', title: 'Button', render: comp('Button', 'button', buttonMd) },
|
|
345
|
+
{ slug: 'button-group', title: 'Button Group', render: comp('Button Group', 'button-group', buttonGroupMd) },
|
|
346
|
+
{ slug: 'toggle', title: 'Toggle', render: comp('Toggle', 'toggle', toggleMd) },
|
|
347
|
+
{ slug: 'toggle-group', title: 'Toggle Group', render: comp('Toggle Group', 'toggle-group', toggleGroupMd) },
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
label: 'Navegação',
|
|
352
|
+
pages: [
|
|
353
|
+
{ slug: 'breadcrumb', title: 'Breadcrumb', render: comp('Breadcrumb', 'breadcrumb', breadcrumbMd) },
|
|
354
|
+
{ slug: 'command', title: 'Command', render: comp('Command', 'command', commandMd) },
|
|
355
|
+
{ slug: 'pagination', title: 'Pagination', render: comp('Pagination', 'pagination', paginationMd) },
|
|
356
|
+
{ slug: 'tabs', title: 'Tabs', render: comp('Tabs', 'tabs', tabsMd) },
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
label: 'Exibição',
|
|
361
|
+
pages: [
|
|
362
|
+
{ slug: 'avatar', title: 'Avatar', render: comp('Avatar', 'avatar', avatarMd) },
|
|
363
|
+
{ slug: 'badge', title: 'Badge', render: comp('Badge', 'badge', badgeMd) },
|
|
364
|
+
{ slug: 'carousel', title: 'Carousel', render: comp('Carousel', 'carousel', carouselMd) },
|
|
365
|
+
{ slug: 'copyable', title: 'Copyable', render: comp('Copyable', 'copyable', copyableMd) },
|
|
366
|
+
{ slug: 'item', title: 'Item', render: comp('Item', 'item', itemMd) },
|
|
367
|
+
{ slug: 'kbd', title: 'Kbd', render: comp('Kbd', 'kbd', kbdMd) },
|
|
368
|
+
{ slug: 'markdown', title: 'Markdown', render: comp('Markdown', 'markdown', markdownMd) },
|
|
369
|
+
{ slug: 'table', title: 'Table', render: comp('Table', 'table', tableMd) },
|
|
370
|
+
{ slug: 'truncate', title: 'Truncate', render: comp('Truncate', 'truncate', truncateMd) },
|
|
371
|
+
],
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
label: 'Feedback',
|
|
375
|
+
pages: [
|
|
376
|
+
{ slug: 'alert', title: 'Alert', render: comp('Alert', 'alert', alertMd) },
|
|
377
|
+
{ slug: 'confirm', title: 'Dialog (imperativo)', render: comp('Dialog (imperativo)', 'confirm', confirmMd) },
|
|
378
|
+
{ slug: 'data-state', title: 'Data State', render: comp('Data State', 'data-state', dataStateMd) },
|
|
379
|
+
{ slug: 'empty', title: 'Empty', render: comp('Empty', 'empty', emptyMd) },
|
|
380
|
+
{ slug: 'progress', title: 'Progress', render: comp('Progress', 'progress', progressMd) },
|
|
381
|
+
{ slug: 'skeleton', title: 'Skeleton', render: comp('Skeleton', 'skeleton', skeletonMd) },
|
|
382
|
+
{ slug: 'spinner', title: 'Spinner', render: comp('Spinner', 'spinner', spinnerMd) },
|
|
383
|
+
{ slug: 'toast', title: 'Toast', render: comp('Toast', 'toast', toastMd) },
|
|
384
|
+
],
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
label: 'Sobreposição',
|
|
388
|
+
pages: [
|
|
389
|
+
{ slug: 'alert-dialog', title: 'Alert Dialog', render: comp('Alert Dialog', 'alert-dialog', alertDialogMd) },
|
|
390
|
+
{ slug: 'dialog', title: 'Dialog', render: comp('Dialog', 'dialog', dialogMd) },
|
|
391
|
+
{ slug: 'drawer', title: 'Drawer', render: comp('Drawer', 'drawer', drawerMd) },
|
|
392
|
+
{ slug: 'menu', title: 'Menu', render: comp('Menu', 'menu', menuMd) },
|
|
393
|
+
{ slug: 'popover', title: 'Popover', render: comp('Popover', 'popover', popoverMd) },
|
|
394
|
+
{ slug: 'tooltip', title: 'Tooltip', render: comp('Tooltip', 'tooltip', tooltipMd) },
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
},
|
|
399
|
+
]
|
|
400
|
+
|
|
401
|
+
/** O catálogo INTEIRO (compat): Maestro (lente) e `/__docs` mostram tudo numa nav só —
|
|
402
|
+
* inclusive o changelog, que no SITE é página do menu superior (fora destas seções);
|
|
403
|
+
* o site divide em duas superfícies de navegação do mesmo SDK (PROTOCOL_SECTIONS em /docs,
|
|
404
|
+
* UI_SECTIONS em /ui). */
|
|
405
|
+
export const DOC_SECTIONS: DocSection[] = [
|
|
406
|
+
{
|
|
407
|
+
...PROTOCOL_SECTIONS[0]!,
|
|
408
|
+
groups: [
|
|
409
|
+
{
|
|
410
|
+
pages: [
|
|
411
|
+
...PROTOCOL_SECTIONS[0]!.groups[0]!.pages,
|
|
412
|
+
{ slug: 'changelog', title: 'Changelog', render: () => <OpusChangelog /> },
|
|
413
|
+
],
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
// A união (/__docs, lente do Maestro) mostra tudo numa nav só e NÃO tem a superfície /ui
|
|
418
|
+
// à parte — então tira a entrada-redirect 'ui' (o catálogo já entra via UI_SECTIONS).
|
|
419
|
+
...PROTOCOL_SECTIONS.slice(1).map((s) => ({
|
|
420
|
+
...s,
|
|
421
|
+
groups: s.groups.map((g) => ({ ...g, pages: g.pages.filter((p) => p.slug !== 'ui') })),
|
|
422
|
+
})),
|
|
423
|
+
...UI_SECTIONS,
|
|
424
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* standalone — monta a doc do DocBrowser como página inteira (providers + topo com toggle de
|
|
3
|
+
* tema). É o que a rota dev `/__docs` de cada app renderiza: a MESMA doc da base, porém
|
|
4
|
+
* dentro do dev server do projeto → veste o tema do projeto e a versão do Opus pinada
|
|
5
|
+
* ali (a "verdade por-projeto", complemento da "verdade da base" no Maestro).
|
|
6
|
+
*
|
|
7
|
+
* O tema (light/dark) NÃO vem daqui: o CSS é importado pelo entry do plugin (o
|
|
8
|
+
* index.css do app) antes de chamar `mountDocs` — por isso a identidade é a do projeto.
|
|
9
|
+
*/
|
|
10
|
+
import { StrictMode, useState } from 'react'
|
|
11
|
+
import { createRoot } from 'react-dom/client'
|
|
12
|
+
import { Moon, Sun } from 'lucide-react'
|
|
13
|
+
import { Button, Toaster, TooltipProvider } from '../react.tsx'
|
|
14
|
+
import { DocBrowser } from './DocBrowser.tsx'
|
|
15
|
+
import type { DocSection } from './registry.tsx'
|
|
16
|
+
|
|
17
|
+
const THEME_KEY = 'opus-theme'
|
|
18
|
+
|
|
19
|
+
// Reexporta o adaptador de pasta: o entry do plugin `opusDocs({ source })` importa
|
|
20
|
+
// `mountDocs` + `docSectionsFromFolder` daqui (mesmo subpath), sem export novo no package.
|
|
21
|
+
export { docSectionsFromFolder } from './folder.tsx'
|
|
22
|
+
export type { FolderOptions } from './folder.tsx'
|
|
23
|
+
|
|
24
|
+
function StandaloneDocBrowser({
|
|
25
|
+
basePath,
|
|
26
|
+
title,
|
|
27
|
+
subtitle,
|
|
28
|
+
sections,
|
|
29
|
+
}: {
|
|
30
|
+
basePath: string
|
|
31
|
+
title: string
|
|
32
|
+
subtitle: string
|
|
33
|
+
sections?: DocSection[]
|
|
34
|
+
}): React.ReactElement {
|
|
35
|
+
const [dark, setDark] = useState<boolean>(() => document.documentElement.classList.contains('dark'))
|
|
36
|
+
const toggle = (): void => {
|
|
37
|
+
const next = !dark
|
|
38
|
+
setDark(next)
|
|
39
|
+
document.documentElement.classList.toggle('dark', next)
|
|
40
|
+
try {
|
|
41
|
+
localStorage.setItem(THEME_KEY, next ? 'dark' : 'light')
|
|
42
|
+
} catch {
|
|
43
|
+
/* localStorage indisponível (modo privado) — o toggle ainda vale na sessão. */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return (
|
|
47
|
+
<div className="flex h-screen flex-col bg-background text-foreground">
|
|
48
|
+
<header className="flex h-12 shrink-0 items-center justify-between border-b border-border px-4">
|
|
49
|
+
<div className="flex items-center gap-1.5 text-sm">
|
|
50
|
+
<span className="font-medium">{title}</span>
|
|
51
|
+
<span className="text-muted-foreground/60">· {subtitle}</span>
|
|
52
|
+
</div>
|
|
53
|
+
<Button variant="ghost" size="icon" aria-label="Alternar tema" onClick={toggle}>
|
|
54
|
+
{dark ? <Sun /> : <Moon />}
|
|
55
|
+
</Button>
|
|
56
|
+
</header>
|
|
57
|
+
<div className="min-h-0 flex-1">
|
|
58
|
+
<DocBrowser basePath={basePath} sections={sections} />
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface MountDocsOptions {
|
|
65
|
+
/** Prefixo das rotas. Default `/__docs`. */
|
|
66
|
+
basePath?: string
|
|
67
|
+
/** Elemento alvo. Default: `#root` (cria um se faltar). */
|
|
68
|
+
el?: HTMLElement
|
|
69
|
+
/** Título no topo. Default `Components` (o site-base passa `opus`). */
|
|
70
|
+
title?: string
|
|
71
|
+
/** Legenda ao lado do título. Default `a verdade visual deste projeto`. */
|
|
72
|
+
subtitle?: string
|
|
73
|
+
/** Seções a exibir. Ausente = a doc do próprio Opus (`DOC_SECTIONS`). Passe o retorno de
|
|
74
|
+
* `docSectionsFromFolder(...)` pra montar a doc a partir da pasta `docs/` do projeto. */
|
|
75
|
+
sections?: DocSection[]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Monta a doc na página. Chamado pelo entry do plugin opusDocs (depois de importar o CSS do app). */
|
|
79
|
+
export function mountDocs(options: MountDocsOptions = {}): void {
|
|
80
|
+
const basePath = options.basePath ?? '/__docs'
|
|
81
|
+
const title = options.title ?? 'Components'
|
|
82
|
+
const subtitle = options.subtitle ?? 'a verdade visual deste projeto'
|
|
83
|
+
// Tema persistido pela própria doc; senão, a preferência do sistema.
|
|
84
|
+
let saved: string | null = null
|
|
85
|
+
try {
|
|
86
|
+
saved = localStorage.getItem(THEME_KEY)
|
|
87
|
+
} catch {
|
|
88
|
+
/* ignora */
|
|
89
|
+
}
|
|
90
|
+
const dark = saved !== null ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
91
|
+
document.documentElement.classList.toggle('dark', dark)
|
|
92
|
+
|
|
93
|
+
let el = options.el ?? document.getElementById('root')
|
|
94
|
+
if (el === null || el === undefined) {
|
|
95
|
+
el = document.createElement('div')
|
|
96
|
+
el.id = 'root'
|
|
97
|
+
document.body.appendChild(el)
|
|
98
|
+
}
|
|
99
|
+
createRoot(el).render(
|
|
100
|
+
<StrictMode>
|
|
101
|
+
<TooltipProvider delayDuration={200}>
|
|
102
|
+
<StandaloneDocBrowser basePath={basePath} title={title} subtitle={subtitle} sections={options.sections} />
|
|
103
|
+
</TooltipProvider>
|
|
104
|
+
<Toaster />
|
|
105
|
+
</StrictMode>,
|
|
106
|
+
)
|
|
107
|
+
}
|