@softize/opus 25.5.0 → 25.7.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.
- package/CHANGELOG.md +50 -1
- package/docs/adr/0017-ui-assumes-a-fixed-desktop-layout.md +19 -0
- package/docs/adr/0056-doc-browser-separa-runtime-leve-do-catalogo.md +8 -0
- package/docs/adr/0057-doc-browser-permite-busca-no-chrome-do-consumidor.md +152 -0
- package/docs/adr/README.md +12 -0
- package/package.json +5 -1
- package/src/ui/components/patterns/sidebar.tsx +32 -11
- package/src/ui/components/primitives/markdown.tsx +2 -11
- package/src/ui/docs/DocBrowser.tsx +404 -69
- package/src/ui/docs/DocCatalog.tsx +255 -0
- package/src/ui/docs/DocHeader.tsx +74 -0
- package/src/ui/docs/browser.tsx +17 -1
- package/src/ui/docs/changelog.tsx +16 -41
- package/src/ui/docs/content/actions.md +3 -3
- package/src/ui/docs/content/customization.md +5 -1
- package/src/ui/docs/content/documentation-portal.md +190 -0
- package/src/ui/docs/content/getting-started.md +129 -87
- package/src/ui/docs/content/installation.md +94 -0
- package/src/ui/docs/content/markdown.md +41 -0
- package/src/ui/docs/content/mcp.md +2 -2
- package/src/ui/docs/content/overview.md +58 -0
- package/src/ui/docs/content/sidebar.md +4 -0
- package/src/ui/docs/folder.tsx +2 -1
- package/src/ui/docs/index.ts +8 -1
- package/src/ui/docs/links.ts +102 -0
- package/src/ui/docs/markdown.tsx +5 -3
- package/src/ui/docs/records.ts +144 -0
- package/src/ui/docs/registry.tsx +149 -59
- package/src/ui/docs/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Changelog
|
|
1
|
+
# Changelog
|
|
2
2
|
|
|
3
3
|
O que muda em cada versão — e, quando quebra, **o que fazer**. Regras de leitura:
|
|
4
4
|
minor = novidade compatível; major = breaking (a seção **Breaking** diz a migração).
|
|
@@ -7,6 +7,55 @@ Depois de qualquer bump, rode os gates (`typecheck` · `test` · `opus check` ·
|
|
|
7
7
|
`opus copy --check` · `base copy check` · `manifest:check`) — eles apontam o que a
|
|
8
8
|
mudança cobra do seu código.
|
|
9
9
|
|
|
10
|
+
## 25.7.0 — 2026-10-07
|
|
11
|
+
|
|
12
|
+
Portais de documentação podem manter o mesmo mapa lateral em guias, ADRs e changelog. A superfície
|
|
13
|
+
leve `@softize/opus/ui/docs/browser` passa a oferecer `DocHeader`, `DocCatalog`, `DocRecordDetail`
|
|
14
|
+
e parsers de decisões e versões, sem carregar o catálogo do SDK nem o compilador de exemplos.
|
|
15
|
+
O host continua responsável por fontes, rotas e autorização.
|
|
16
|
+
|
|
17
|
+
`DocBrowser` aceita busca externa controlada, largura inicial da lateral, grupos inicialmente
|
|
18
|
+
recolhidos, destinos globais, páginas fora da árvore e conteúdo do host no lugar de um guia. O modo
|
|
19
|
+
editorial acrescenta contexto, sumário dos títulos renderizados e continuidade entre páginas.
|
|
20
|
+
A busca revela resultados sem perder a escolha manual dos ramos abertos. As novas propriedades
|
|
21
|
+
são opcionais: os consumidores existentes preservam a composição e os defaults anteriores.
|
|
22
|
+
|
|
23
|
+
O cabeçalho compartilhado mantém a busca documental compacta e centralizada. Os catálogos têm
|
|
24
|
+
busca textual própria dentro da página, independente do topo. ADRs apresentam contexto curto;
|
|
25
|
+
changelog mantém o texto das versões e destaca cuidados de atualização. Não há tabela, filtros,
|
|
26
|
+
controles de ordenação ou paginação nesses índices estáticos.
|
|
27
|
+
|
|
28
|
+
A adaptação à largura da tela é restrita ao cabeçalho, à leitura editorial e aos catálogos do
|
|
29
|
+
portal documental. A UI das aplicações e os componentes demonstrados nos exemplos vivos continuam
|
|
30
|
+
com o contrato desktop a partir de `64rem` da ADR 0017.
|
|
31
|
+
|
|
32
|
+
O portal do Opus passa a abrir pela documentação, com Visão geral, Instalação e Primeiros passos
|
|
33
|
+
antes das referências. ADRs e Changelog entram na lateral; os endereços anteriores dos guias e do
|
|
34
|
+
catálogo de UI são preservados. Não há migração obrigatória para quem não adotar essas composições.
|
|
35
|
+
|
|
36
|
+
Links de `SidebarItem` podem declarar `href`, com o tratamento de foco dos botões e abertura em
|
|
37
|
+
outra aba; sem esse campo, o item continua sendo um botão. A consulta do topo preserva a posição
|
|
38
|
+
de leitura dos registros. O changelog limita os cuidados de atualização à seção `Breaking`, sem
|
|
39
|
+
incluir novidades ou correções seguintes; resumos e detalhes preservam a ordem original das seções.
|
|
40
|
+
O changelog embutido usa **Breaking: cuidados ao atualizar** no lugar de **Breaking — o que fazer**.
|
|
41
|
+
O catálogo usa esse mesmo rótulo.
|
|
42
|
+
|
|
43
|
+
## 25.6.0 — 2026-10-07
|
|
44
|
+
|
|
45
|
+
Aplicações que apresentam Markdown do próprio produto podem usar
|
|
46
|
+
`@softize/opus/ui/docs/links` para resolver caminhos relativos, derivar slugs e normalizar
|
|
47
|
+
fragmentos sem carregar React. A entrada `@softize/opus/ui/docs/browser` também reexporta esses
|
|
48
|
+
utilitários. O renderizador compartilhado usa a mesma normalização nos IDs dos títulos, inclusive
|
|
49
|
+
quando o texto contém hífens cercados por espaços ou repetidos; assim, links e headings deixam de
|
|
50
|
+
divergir nesses casos.
|
|
51
|
+
|
|
52
|
+
Não há migração geral obrigatória. Links profundos já salvos para títulos com hífen cercado por
|
|
53
|
+
espaços ou com hífens consecutivos precisam usar um único hífen; por exemplo, `a---b` passa a ser
|
|
54
|
+
`a-b`. Consumidores que já implementam essa gramática localmente podem substituí-la pelos
|
|
55
|
+
utilitários compartilhados e continuar decidindo a rota e o espaço de destino. O resolvedor aceita
|
|
56
|
+
fontes `.md` e `.mdx`; o caminho atual é relativo à raiz documental, e apenas `/docs/` com barra
|
|
57
|
+
inicial declara essa raiz num link absoluto.
|
|
58
|
+
|
|
10
59
|
## 25.5.0 — 2026-10-07
|
|
11
60
|
|
|
12
61
|
Aplicações que publicam documentação própria podem usar a nova entrada
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- **Status:** aceita.
|
|
4
4
|
- **Data:** 2026-09-15.
|
|
5
|
+
- **Emenda:** 2026-10-07; portal documental delimitado pela ADR 0057.
|
|
5
6
|
|
|
6
7
|
## Contexto
|
|
7
8
|
|
|
@@ -28,6 +29,22 @@ Footers de formulário modal dimensionam as ações pelo conteúdo e usam `Cance
|
|
|
28
29
|
`distribution="equal"` permanece uma escolha explícita para decisões de peso equivalente e,
|
|
29
30
|
nesse caso, o cancelamento usa `outline`.
|
|
30
31
|
|
|
32
|
+
### Exceção: portal de documentação
|
|
33
|
+
|
|
34
|
+
O portal documental tem uma jornada de leitura distinta da UI operacional das aplicações. Conforme
|
|
35
|
+
a ADR 0057, `DocHeader`, a leitura editorial de `DocBrowser` e `DocCatalog`/`DocRecordDetail`
|
|
36
|
+
podem adaptar cabeçalho, espaçamento e sumário à largura da tela. A exceção foi aprovada para
|
|
37
|
+
preservar a composição validada nos portais do Opus e da Grand Brasil.
|
|
38
|
+
|
|
39
|
+
A exceção não torna responsivos `Split`, `Sidebar`, formulários, tabelas ou outros componentes da
|
|
40
|
+
UI das aplicações. O host documental continua responsável pela composição de sua moldura em tela
|
|
41
|
+
estreita. Exemplos vivos usam o mesmo contrato desktop dos componentes demonstrados; o portal não
|
|
42
|
+
promete que todos os exemplos sejam adequados a telas menores que `64rem`.
|
|
43
|
+
|
|
44
|
+
O gate permite variantes de viewport somente nos três arquivos documentais identificados na ADR
|
|
45
|
+
0057. Arquivos novos, mesmo dentro de `docs/`, continuam sujeitos à regra até uma decisão explícita.
|
|
46
|
+
Container queries não entram na exceção.
|
|
47
|
+
|
|
31
48
|
## Consequências
|
|
32
49
|
|
|
33
50
|
- O mesmo componente preserva a composição a partir de `64rem`, inclusive dentro de panes,
|
|
@@ -50,5 +67,7 @@ nesse caso, o cancelamento usa `outline`.
|
|
|
50
67
|
## Verificação
|
|
51
68
|
|
|
52
69
|
- Um teste de inventário reprova variantes de largura e container queries na UI viva.
|
|
70
|
+
- O mesmo teste delimita a exceção documental por arquivo e comprova que ela não alcança componentes
|
|
71
|
+
da aplicação, outros arquivos documentais ou container queries.
|
|
53
72
|
- Testes estruturais fixam layouts, larguras de modal e distribuição dos footers.
|
|
54
73
|
- Documentação pública declara a largura mínima, o default por conteúdo e a exceção `equal`.
|
|
@@ -43,6 +43,12 @@ O renderizador compartilhado pode reescrever links e criar IDs estáveis para t
|
|
|
43
43
|
opções são explícitas; o caminho usado em conversas continua sem IDs e sem tratamento editorial
|
|
44
44
|
quando elas não são pedidas. Frontmatter é removido somente nas superfícies documentais.
|
|
45
45
|
|
|
46
|
+
O subpath leve `@softize/opus/ui/docs/links` publica a normalização de âncoras, a derivação de slug a
|
|
47
|
+
partir do caminho da fonte e a resolução de links Markdown relativos sem carregar React. A entrada
|
|
48
|
+
`browser` também os reexporta por conveniência. O renderizador e os consumidores usam essas funções
|
|
49
|
+
em comum: uma aplicação decide em qual espaço ou superfície abrir a fonte resolvida, mas não
|
|
50
|
+
reimplementa a gramática de caminhos, prefixos numéricos e fragmentos.
|
|
51
|
+
|
|
46
52
|
### 4. A superfície permanece no pacote Opus
|
|
47
53
|
|
|
48
54
|
O runtime leve continua em `@softize/opus` enquanto compartilha dependências, manutenção e ciclo
|
|
@@ -73,5 +79,7 @@ existir evidência operacional para sustentar essa separação.
|
|
|
73
79
|
- Classificação, autorização e conteúdo permanecem responsabilidade do consumidor.
|
|
74
80
|
- A busca é local e recebe o conteúdo no navegador; ela não substitui filtragem no servidor quando
|
|
75
81
|
houver documentos com audiências diferentes.
|
|
82
|
+
- Links Markdown relativos, slugs e fragmentos seguem uma gramática compartilhada; o consumidor
|
|
83
|
+
continua responsável por mapear a fonte resolvida para seu espaço, sua rota e sua política.
|
|
76
84
|
- Um teste de arquitetura percorre o grafo do novo subpath e reprova se ele alcançar catálogo,
|
|
77
85
|
exemplos vivos ou o compilador correspondente.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# ADR 0057 — Portal documental compartilha navegação, busca e leitura
|
|
2
|
+
|
|
3
|
+
- **Status:** aceita para implementação local; publicação pendente.
|
|
4
|
+
- **Data:** 2026-10-07.
|
|
5
|
+
|
|
6
|
+
## Contexto
|
|
7
|
+
|
|
8
|
+
Portais de documentação precisam apresentar guias, decisões e entregas sem trocar o mapa de
|
|
9
|
+
navegação a cada destino. O `DocBrowser` já organiza páginas, busca e árvore, mas sua composição
|
|
10
|
+
original mantém o campo na lateral e não permite usar o cabeçalho ou substituir a leitura por um
|
|
11
|
+
catálogo. Os portais do Opus e da Grand Brasil precisam dessas variações.
|
|
12
|
+
|
|
13
|
+
Copiar a árvore, a filtragem e o grid em cada aplicação cria comportamentos concorrentes e repete
|
|
14
|
+
defeitos de layout. Incorporar marca, rotas e autorização ao navegador, por outro lado, faria o SDK
|
|
15
|
+
assumir decisões do produto hospedeiro. A extensão precisa compartilhar a composição sem transferir
|
|
16
|
+
essas responsabilidades.
|
|
17
|
+
|
|
18
|
+
## Decisão vigente
|
|
19
|
+
|
|
20
|
+
A superfície leve `@softize/opus/ui/docs/browser` oferece navegação, cabeçalho e leitura para fontes
|
|
21
|
+
fornecidas pelo host, sem importar o catálogo do Opus nem o compilador de exemplos vivos. A entrada
|
|
22
|
+
histórica `@softize/opus/ui/docs` mantém o catálogo completo e reexporta a mesma composição.
|
|
23
|
+
|
|
24
|
+
### Cabeçalho e busca documental
|
|
25
|
+
|
|
26
|
+
`DocHeader` recebe marca, busca, ações e navegação opcional. Centraliza a geometria do topo, com
|
|
27
|
+
busca compacta de até `22rem`, colunas simétricas nas extremidades e uma linha própria para o campo
|
|
28
|
+
em telas menores que `64rem`. Não conhece tema, repositório, rotas ou estado de pesquisa.
|
|
29
|
+
|
|
30
|
+
`DocBrowser.search` mantém duas composições:
|
|
31
|
+
|
|
32
|
+
- `placement: "sidebar"` preserva o campo e o estado local existentes.
|
|
33
|
+
- `placement: "external"` recebe o valor controlado pelo host, filtra a mesma árvore e não
|
|
34
|
+
renderiza um segundo campo.
|
|
35
|
+
|
|
36
|
+
Nos dois portais, a busca documental mantém rótulo, apresentação, valor e função em guias,
|
|
37
|
+
catálogos e registros. Pesquisa os guias na lateral; não se transforma silenciosamente numa busca
|
|
38
|
+
de ADRs ou versões.
|
|
39
|
+
|
|
40
|
+
### Hierarquia e leitura
|
|
41
|
+
|
|
42
|
+
`sidebarSize` configura a largura inicial usando o `PaneSize` do `Split`. A omissão preserva
|
|
43
|
+
`14rem`. `sidebarInitiallyCollapsed` permite começar com seções e grupos nomeados recolhidos,
|
|
44
|
+
inclusive quando a pessoa abre uma página por URL direta. A busca revela os resultados
|
|
45
|
+
temporariamente; limpá-la restaura os ramos abertos manualmente.
|
|
46
|
+
|
|
47
|
+
`sidebarLinks` apresenta destinos globais após os capítulos, no mesmo nível visual e fora da
|
|
48
|
+
sequência anterior/próxima. `active` identifica o catálogo atual. `navigationTitle` oferece um
|
|
49
|
+
rótulo curto para uma página sem alterar seu título completo ou os termos pesquisáveis.
|
|
50
|
+
|
|
51
|
+
Esses destinos usam `SidebarItem.href`, não uma cópia do estilo da linha. A extensão preserva o
|
|
52
|
+
botão quando `href` é omitido, o foco nos dois modos e a abertura nativa em outra aba. Quando há
|
|
53
|
+
`onClick`, somente a navegação simples é interceptada; o host mantém a decisão de roteamento.
|
|
54
|
+
|
|
55
|
+
O modo opcional `editorial` acrescenta contexto, sumário dos headings realmente renderizados e
|
|
56
|
+
links de continuidade segundo a ordem declarada. `additionalPages` preserva URLs de páginas fora
|
|
57
|
+
da árvore e da sequência. O renderer de exemplos aceita `codeCollapsible={false}` quando ocultar
|
|
58
|
+
o código interromperia uma explicação didática.
|
|
59
|
+
|
|
60
|
+
### Catálogos e registros
|
|
61
|
+
|
|
62
|
+
`content` substitui a área de leitura sem desmontar a árvore. Quando esse slot é fornecido, o
|
|
63
|
+
host resolve rota e título; o navegador não escolhe uma página dos guias nem aplica seus redirects.
|
|
64
|
+
`null` representa uma leitura intencionalmente vazia. `sidebar={false}` permite atender
|
|
65
|
+
consumidores que precisem de uma página isolada, mas os dois portais mantêm a lateral em
|
|
66
|
+
guias, catálogos e registros.
|
|
67
|
+
|
|
68
|
+
`DocCatalog` oferece leitura editorial de registros estáticos, sem tabela, filtros, ordenação
|
|
69
|
+
configurável ou paginação. ADRs apresentam número, título, metadata declarada e o primeiro
|
|
70
|
+
parágrafo do contexto. Changelog apresenta texto integral por versão, com data e cuidados de
|
|
71
|
+
atualização em destaque. Os registros mais recentes aparecem primeiro.
|
|
72
|
+
|
|
73
|
+
Cada catálogo tem um campo textual na própria página. `query` e `onQueryChange` são controlados
|
|
74
|
+
pelo host, independentemente da busca documental. Os portais preservam a consulta local ao abrir
|
|
75
|
+
um registro e voltar, e a limpam ao trocar de catálogo; não limpam a consulta do topo pela navegação.
|
|
76
|
+
|
|
77
|
+
`DocRecordDetail` apresenta somente o nome do catálogo antes do título, sem breadcrumb ou retorno
|
|
78
|
+
duplicado. A navegação permanece na lateral. Para preservar os ramos abertos, o host mantém a mesma
|
|
79
|
+
instância de `DocBrowser` entre guia, catálogo e registro.
|
|
80
|
+
|
|
81
|
+
Os parsers projetam as mesmas fontes Markdown, sem cadastro editorial paralelo. Não inferem data
|
|
82
|
+
ou status ausentes, resumo sem seção de contexto ou releases anteriores ao versionamento.
|
|
83
|
+
Ressalvas do status permanecem no texto integral da ADR.
|
|
84
|
+
|
|
85
|
+
Este índice estático é uma exceção deliberada ao uso de `ActionList`: publica arquivos versionados
|
|
86
|
+
e funciona sem API da aplicação. Não substitui coleções de recursos servidas por actions.
|
|
87
|
+
|
|
88
|
+
### Relação com o contrato desktop da UI
|
|
89
|
+
|
|
90
|
+
A composição documental é uma exceção explícita à ADR 0017: `DocHeader.tsx`, `DocBrowser.tsx`
|
|
91
|
+
(na leitura editorial) e `DocCatalog.tsx` (incluindo `DocRecordDetail`) podem usar variantes de
|
|
92
|
+
viewport para organizar cabeçalho, sumário e espaçamento. A exceção fica restrita a esses arquivos;
|
|
93
|
+
não libera toda a pasta `docs/` nem container queries. O gate verifica as funções de composição
|
|
94
|
+
permitidas, sem liberar outras funções ou a montagem de `Split` no mesmo arquivo.
|
|
95
|
+
|
|
96
|
+
Essa adaptação foi aprovada para o portal de leitura, não para a UI das aplicações. O host organiza
|
|
97
|
+
sua moldura em telas estreitas; `Split`, `Sidebar` e os exemplos vivos não ganham um contrato
|
|
98
|
+
responsivo por estarem dentro da documentação. Manter o portal inteiramente desktop descartaria
|
|
99
|
+
a composição já validada; ampliar a exceção para toda a UI alteraria uma decisão fora deste escopo.
|
|
100
|
+
|
|
101
|
+
## Alternativas consideradas
|
|
102
|
+
|
|
103
|
+
### Manter campo, largura e leitura fixos
|
|
104
|
+
|
|
105
|
+
Preservaria uma API menor, mas obrigaria portais autônomos a copiar a árvore ou adotar a composição
|
|
106
|
+
do catálogo embutido. As novas propriedades são opcionais e mantêm os defaults existentes.
|
|
107
|
+
|
|
108
|
+
### Compor todo o portal dentro de DocBrowser
|
|
109
|
+
|
|
110
|
+
Centralizaria a implementação, ao custo de misturar identidade visual, navegação global,
|
|
111
|
+
autorização e rotas do host com a leitura documental. `DocHeader` compartilha somente a geometria;
|
|
112
|
+
`content` compartilha o mapa sem decidir os destinos.
|
|
113
|
+
|
|
114
|
+
### Copiar grids e filtragem nos consumidores
|
|
115
|
+
|
|
116
|
+
Evitaria a extensão do pacote, mas os primeiros consumidores já repetiram um desalinhamento do
|
|
117
|
+
cabeçalho. `PageHeader` e `PageShell` organizam páginas de recursos, não o topo de um portal
|
|
118
|
+
documental. Manter essas cópias deixaria o mesmo defeito com duas correções locais.
|
|
119
|
+
|
|
120
|
+
### Tabela para decisões e entregas
|
|
121
|
+
|
|
122
|
+
Foi implementada no protótipo com filtro, ordenação e paginação, mas rejeitada na avaliação visual.
|
|
123
|
+
ADRs precisam de contexto para escolher a leitura; changelog precisa manter a continuidade do texto.
|
|
124
|
+
A busca textual atende a consulta sem acrescentar controles de coleção.
|
|
125
|
+
|
|
126
|
+
### Ocultar a lateral e trocar o alcance da busca no catálogo
|
|
127
|
+
|
|
128
|
+
Também foi experimentado e substituído após avaliação. Ocultar a lateral interrompia o mapa;
|
|
129
|
+
trocar o alcance do campo superior tornava a pesquisa ambígua. A decisão vigente mantém a lateral
|
|
130
|
+
e separa a consulta documental da consulta local.
|
|
131
|
+
|
|
132
|
+
## Consequências
|
|
133
|
+
|
|
134
|
+
- Consumidores existentes preservam campo lateral, largura de `14rem` e composição não editorial.
|
|
135
|
+
- A biblioteca mantém uma árvore e uma regra de busca; o host mantém conteúdo, rotas e autorização.
|
|
136
|
+
- Os dois portais compartilham cabeçalho e catálogos sem compartilhar sua taxonomia específica.
|
|
137
|
+
- Guias, índices e fontes acessíveis por URL direta precisam ser filtrados antes de chegar ao
|
|
138
|
+
navegador quando houver restrição de acesso. Esta composição não é uma fronteira de segurança.
|
|
139
|
+
- A consolidação é um candidato local. A publicação e a adoção pela Grand Brasil são etapas
|
|
140
|
+
distintas, posteriores à revisão independente.
|
|
141
|
+
|
|
142
|
+
## Verificação
|
|
143
|
+
|
|
144
|
+
- Testes de `DocBrowser`: busca local e externa, vazio, largura configurável, lateral opcional,
|
|
145
|
+
persistência de grupos, expansão temporária, conteúdo do host, redirects e leitura editorial.
|
|
146
|
+
- Testes de `DocHeader`: slots, atributos nativos e composição com ou sem busca e navegação.
|
|
147
|
+
- Testes de `DocCatalog`: busca local controlada, independência do topo, ausência de controles de
|
|
148
|
+
coleção, texto integral das versões, incompatibilidades e abertura nativa em outra aba.
|
|
149
|
+
- Testes dos parsers: metadata ausente, resumo canônico, acentos e termos combinados, texto integral
|
|
150
|
+
e ausência de versões retroativas fabricadas.
|
|
151
|
+
- Inspeção dos dois portais: largura estreita, alinhamento do topo, lateral persistente, busca,
|
|
152
|
+
URLs diretas, ida ao registro e retorno ao catálogo.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Decisões arquiteturais
|
|
3
|
+
order: -1
|
|
4
|
+
---
|
|
5
|
+
# Decisões arquiteturais
|
|
6
|
+
|
|
7
|
+
As ADRs registram o contexto, as alternativas e as consequências das decisões estruturais do
|
|
8
|
+
Opus. Use a Documentação para compreender o contrato vigente; consulte esta área quando precisar
|
|
9
|
+
entender por que uma restrição existe ou como ela evoluiu.
|
|
10
|
+
|
|
11
|
+
Uma ADR preserva seu contexto histórico mesmo quando é substituída. O comportamento vigente deve
|
|
12
|
+
estar documentado na superfície correspondente, sem exigir a reconstrução dessa sequência.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softize/opus",
|
|
3
|
-
"version": "25.
|
|
3
|
+
"version": "25.7.0",
|
|
4
4
|
"description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -221,6 +221,10 @@
|
|
|
221
221
|
"types": "./src/ui/docs/browser.tsx",
|
|
222
222
|
"default": "./src/ui/docs/browser.tsx"
|
|
223
223
|
},
|
|
224
|
+
"./ui/docs/links": {
|
|
225
|
+
"types": "./src/ui/docs/links.ts",
|
|
226
|
+
"default": "./src/ui/docs/links.ts"
|
|
227
|
+
},
|
|
224
228
|
"./ui/docs/standalone": {
|
|
225
229
|
"types": "./src/ui/docs/standalone.tsx",
|
|
226
230
|
"default": "./src/ui/docs/standalone.tsx"
|
|
@@ -77,7 +77,9 @@ export interface SidebarItemProps {
|
|
|
77
77
|
/** Complemento do tooltip no rail recolhido. */
|
|
78
78
|
tooltipHint?: ReactNode;
|
|
79
79
|
className?: string;
|
|
80
|
-
onClick
|
|
80
|
+
/** Destino nativo. Com onClick, intercepta somente a navegação simples; preserva novas abas. */
|
|
81
|
+
href?: string;
|
|
82
|
+
onClick?: () => void;
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
/** Linha única de navegação: o alvo principal e seus controles são irmãos, nunca botões aninhados. */
|
|
@@ -95,8 +97,29 @@ export function SidebarItem({
|
|
|
95
97
|
tooltipHint,
|
|
96
98
|
className,
|
|
97
99
|
onClick,
|
|
100
|
+
href,
|
|
98
101
|
}: SidebarItemProps): React.ReactElement {
|
|
99
102
|
const collapsed = useSidebarCollapsed();
|
|
103
|
+
const Target = href !== undefined && !disabled ? "a" : "button";
|
|
104
|
+
const targetProps =
|
|
105
|
+
Target === "a" ? { href } : { type: "button" as const, disabled };
|
|
106
|
+
const activate = (
|
|
107
|
+
event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>,
|
|
108
|
+
) => {
|
|
109
|
+
if (href !== undefined) {
|
|
110
|
+
if (
|
|
111
|
+
event.button !== 0 ||
|
|
112
|
+
event.metaKey ||
|
|
113
|
+
event.ctrlKey ||
|
|
114
|
+
event.shiftKey ||
|
|
115
|
+
event.altKey ||
|
|
116
|
+
!onClick
|
|
117
|
+
)
|
|
118
|
+
return;
|
|
119
|
+
event.preventDefault();
|
|
120
|
+
}
|
|
121
|
+
onClick?.();
|
|
122
|
+
};
|
|
100
123
|
const hasActions = actions !== undefined && actions !== null;
|
|
101
124
|
// O rótulo cortado esmaece em vez de terminar em reticências; a medição do transbordo
|
|
102
125
|
// mantém a máscara fora do rótulo que cabe inteiro na linha.
|
|
@@ -104,12 +127,11 @@ export function SidebarItem({
|
|
|
104
127
|
useOverflowing<HTMLSpanElement>(label);
|
|
105
128
|
if (collapsed) {
|
|
106
129
|
const button = (
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
disabled={disabled}
|
|
130
|
+
<Target
|
|
131
|
+
{...targetProps}
|
|
110
132
|
aria-label={label}
|
|
111
133
|
aria-current={active ? "page" : undefined}
|
|
112
|
-
onClick={
|
|
134
|
+
onClick={activate}
|
|
113
135
|
className={cn(
|
|
114
136
|
"mx-auto grid size-10 place-items-center rounded-md transition-colors disabled:pointer-events-none disabled:opacity-40",
|
|
115
137
|
"outline-none",
|
|
@@ -125,7 +147,7 @@ export function SidebarItem({
|
|
|
125
147
|
>
|
|
126
148
|
{icon}
|
|
127
149
|
</span>
|
|
128
|
-
</
|
|
150
|
+
</Target>
|
|
129
151
|
);
|
|
130
152
|
// `className` vai no MESMO elemento nos dois modos — a linha —, senão a prop mudaria de alvo
|
|
131
153
|
// conforme o rail estivesse recolhido, e o consumidor não teria como saber o que estilizou.
|
|
@@ -209,11 +231,10 @@ export function SidebarItem({
|
|
|
209
231
|
)}
|
|
210
232
|
</span>
|
|
211
233
|
)}
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
disabled={disabled}
|
|
234
|
+
<Target
|
|
235
|
+
{...targetProps}
|
|
215
236
|
aria-current={active ? "page" : undefined}
|
|
216
|
-
onClick={
|
|
237
|
+
onClick={activate}
|
|
217
238
|
className={cn(
|
|
218
239
|
"flex min-w-0 flex-1 items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left text-sm disabled:pointer-events-none",
|
|
219
240
|
"outline-none",
|
|
@@ -254,7 +275,7 @@ export function SidebarItem({
|
|
|
254
275
|
{badge}
|
|
255
276
|
</span>
|
|
256
277
|
)}
|
|
257
|
-
</
|
|
278
|
+
</Target>
|
|
258
279
|
{hasActions && (
|
|
259
280
|
<div
|
|
260
281
|
data-slot="sidebar-item-actions"
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import * as React from 'react'
|
|
14
14
|
import MarkdownIt from 'markdown-it'
|
|
15
|
+
import { documentationHeadingAnchor } from '../../docs/links.ts'
|
|
15
16
|
|
|
16
17
|
/** A instância ÚNICA — a doc (`DocMarkdown`) importa esta mesma. Duas instâncias com
|
|
17
18
|
* opções próprias divergiriam no primeiro edge case, e "mesmo motor" viraria promessa. */
|
|
@@ -60,16 +61,6 @@ function resolveTokenLinks(tokens: ReturnType<typeof markdownIt.parse>, resolveH
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
function headingId(value: string): string {
|
|
64
|
-
return value
|
|
65
|
-
.normalize('NFD')
|
|
66
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
67
|
-
.toLocaleLowerCase('pt-BR')
|
|
68
|
-
.replace(/[^a-z0-9\s-]/g, '')
|
|
69
|
-
.trim()
|
|
70
|
-
.replace(/\s+/g, '-')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
64
|
function visibleInlineText(tokens: ReturnType<typeof markdownIt.parse>): string {
|
|
74
65
|
return tokens
|
|
75
66
|
.map((token) => {
|
|
@@ -87,7 +78,7 @@ function identifyHeadings(tokens: ReturnType<typeof markdownIt.parse>): void {
|
|
|
87
78
|
const content = tokens[index + 1]
|
|
88
79
|
if (opening?.type !== 'heading_open' || content?.type !== 'inline') continue
|
|
89
80
|
const text = visibleInlineText(content.children ?? [])
|
|
90
|
-
const base =
|
|
81
|
+
const base = documentationHeadingAnchor(text)
|
|
91
82
|
if (base === '') continue
|
|
92
83
|
let candidate = base
|
|
93
84
|
let suffix = 1
|