@softize/opus 12.6.1 → 12.6.3
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 +14 -0
- package/bin/lib/postinstall-target.mjs +32 -0
- package/bin/lib/postinstall.mjs +2 -0
- package/package.json +1 -1
- package/registry/skills/build-opus-ui/SKILL.md +12 -6
- package/registry/skills/build-opus-ui/references/evaluations.md +8 -0
- package/registry/skills/build-opus-ui/references/ui-patterns.md +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ 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
|
+
## 12.6.3 — 2026-08-26
|
|
11
|
+
|
|
12
|
+
A skill `build-opus-ui` passa a orientar operacionalmente os patterns entregues em 12.6.0 e
|
|
13
|
+
12.6.1: composição com `ContentHeader`, teto padrão de `72rem` em `Page`, uso isolado de
|
|
14
|
+
`ActionFilterBar`, estrutura de `ItemGroup`, modos atuais de `ActionForm` e `ActionList`, diferença
|
|
15
|
+
entre `Empty` e vazio estrutural, alinhamento explícito de colunas numéricas e cancelamento `ghost`
|
|
16
|
+
em modais. As avaliações da skill agora cobram essas decisões em conjunto.
|
|
17
|
+
|
|
18
|
+
## 12.6.2 — 2026-08-26
|
|
19
|
+
|
|
20
|
+
O `postinstall` passa a materializar `base.json` e `opus.json` somente a partir do exemplar de
|
|
21
|
+
`@softize/opus` resolvido pelo workspace declarado. Versões transitivas instaladas no mesmo projeto
|
|
22
|
+
não disputam mais esses marcadores durante instalações concorrentes.
|
|
23
|
+
|
|
10
24
|
## 12.6.1 — 2026-08-26
|
|
11
25
|
|
|
12
26
|
Os padrões de UI passam a distinguir melhor estrutura e ação. `Page` centraliza o conteúdo com
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs'
|
|
2
|
+
import { createRequire } from 'node:module'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
import { readProjectFile, safeProjectPath } from '@softize/base/project-path'
|
|
6
|
+
|
|
7
|
+
function resolutionDirectory(projectDir) {
|
|
8
|
+
try {
|
|
9
|
+
const base = JSON.parse(readProjectFile(projectDir, 'base.json', { allowMissing: true }).content || '{}')
|
|
10
|
+
const resolveFrom = base?.packages?.['@softize/opus']?.resolveFrom
|
|
11
|
+
if (typeof resolveFrom !== 'string') return projectDir
|
|
12
|
+
const candidate = safeProjectPath(projectDir, resolveFrom, { mustExist: true })
|
|
13
|
+
return candidate.kind === 'directory' ? candidate.path : projectDir
|
|
14
|
+
} catch {
|
|
15
|
+
return projectDir
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Só o exemplar resolvido pelo workspace declarado pode materializar o projeto.
|
|
21
|
+
* Gerenciadores de pacote executam o postinstall de exemplares transitivos com o mesmo
|
|
22
|
+
* INIT_CWD; sem esta prova, versões diferentes disputam base.json e opus.json.
|
|
23
|
+
*/
|
|
24
|
+
export function isResolvedOpusInstance(projectDir, packageRoot) {
|
|
25
|
+
try {
|
|
26
|
+
const from = resolutionDirectory(projectDir)
|
|
27
|
+
const resolved = createRequire(path.join(from, 'package.json')).resolve('@softize/opus/package.json')
|
|
28
|
+
return realpathSync(path.dirname(resolved)) === realpathSync(packageRoot)
|
|
29
|
+
} catch {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
}
|
package/bin/lib/postinstall.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
19
19
|
import { canonicalProjectDirectory, readProjectFile } from '@softize/base/project-path'
|
|
20
20
|
|
|
21
21
|
import { initProject } from './init.mjs'
|
|
22
|
+
import { isResolvedOpusInstance } from './postinstall-target.mjs'
|
|
22
23
|
|
|
23
24
|
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..')
|
|
24
25
|
const REGISTRY_DIR = path.join(PACKAGE_ROOT, 'registry')
|
|
@@ -40,6 +41,7 @@ async function main() {
|
|
|
40
41
|
if (pkg.name === '@softize/opus') return
|
|
41
42
|
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) }
|
|
42
43
|
if (!deps['@softize/opus']) return // Não é consumidor do Opus.
|
|
44
|
+
if (!isResolvedOpusInstance(projectDir, PACKAGE_ROOT)) return
|
|
43
45
|
|
|
44
46
|
const r = await initProject(REGISTRY_DIR, projectDir)
|
|
45
47
|
if (!r.materialization.ok) {
|
package/package.json
CHANGED
|
@@ -17,24 +17,30 @@ existentes, mantendo navegação observável e componentes reutilizáveis sem la
|
|
|
17
17
|
2. Usar os hooks e componentes do catálogo `@softize/opus/ui/react` adequados ao kind.
|
|
18
18
|
3. Tratar página, filtro, seleção e modal importante como estado navegável por URL quando
|
|
19
19
|
o produto precisa de deep link, back/forward ou refresh.
|
|
20
|
-
4.
|
|
20
|
+
4. Compor páginas com `Page` e seu teto centralizado padrão de `72rem`. Usar
|
|
21
|
+
`ContentHeader` em seções que precisam da mesma estrutura de título, descrição,
|
|
22
|
+
metadados e ações; ajustar `level` pela hierarquia semântica, não pelo destaque visual.
|
|
23
|
+
5. Manter margem e posicionamento no consumidor; componente reutilizável controla apenas
|
|
21
24
|
seu interior.
|
|
22
|
-
|
|
25
|
+
6. Não definir a fonte raiz em uma biblioteca ou componente. O navegador e a aplicação são
|
|
23
26
|
responsáveis por `font-size` em `html`; medidas escaláveis da UI usam `rem` ou a escala
|
|
24
27
|
relativa do Tailwind. Reservar `px` a hairlines e compensações presas à geometria da borda,
|
|
25
28
|
com justificativa e cobertura explícitas.
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
7. Evoluir um pattern compartilhado apenas quando a recorrência e o contrato estiverem claros.
|
|
30
|
+
8. Tratar tokens de superfície como pares indivisíveis no mesmo fragmento de classes:
|
|
28
31
|
`bg-card text-card-foreground` e `bg-popover text-popover-foreground`. Não depender da
|
|
29
32
|
igualdade atual com `--foreground`, porque o app pode sobrescrever cada par.
|
|
30
|
-
|
|
33
|
+
9. Usar diretamente a escala `rounded-*`; não criar radius por nome de componente quando
|
|
31
34
|
`rounded-xs` a `rounded-2xl` já expressam a forma. Escolher o degrau pela escala visual:
|
|
32
35
|
detalhes e itens internos tendem a `xs`/`sm`, controles e flutuantes compactos a `md`,
|
|
33
36
|
molduras estruturais a `lg`, e superfícies principais ou modais a `xl`. Essa associação
|
|
34
37
|
orienta o default, não cria uma restrição semântica. Em aninhamento, evitar moldura dupla e
|
|
35
38
|
reduzir o raio interno; em grupos conectados, remover os raios das arestas internas. Tamanho
|
|
36
39
|
e forma permanecem eixos separados; usar `shape="pill"` quando a pílula for intencional.
|
|
37
|
-
|
|
40
|
+
10. Distinguir uma região disponível para criação ou vínculo, representada por `Empty` com
|
|
41
|
+
moldura tracejada, de um resultado vazio dentro de uma estrutura existente, que preserva
|
|
42
|
+
a moldura sólida dessa estrutura.
|
|
43
|
+
11. Testar estados de loading, vazio, erro, sucesso, permissão e interação relevante.
|
|
38
44
|
|
|
39
45
|
## Verificação
|
|
40
46
|
|
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
- Dispara: “Monte a tela de edição usando a form action do Opus.”
|
|
4
4
|
- Não dispara: “Ajuste o CSS de um e-mail estático.”
|
|
5
5
|
- Execução: implementar uma lista com modal roteável e provar loading, erro, vazio e back.
|
|
6
|
+
- Execução estrutural: montar uma página de relatório com `Page`, `ContentHeader` em uma seção,
|
|
7
|
+
`ActionFilterBar` separado do renderer, `ItemGroup` para uma coleção secundária e um
|
|
8
|
+
`ActionFormDialog`; provar teto padrão de `72rem`, hierarquia por `level`, vazio estrutural
|
|
9
|
+
sólido, `Empty` apenas para criação ou vínculo, números sem alinhamento inferido e cancelamento
|
|
10
|
+
`ghost` no modal.
|
|
6
11
|
- Execução de forma: compor uma tabela dentro de Card sem moldura duplicada, manter a moldura
|
|
7
12
|
estrutural standalone em `rounded-lg` e os controles internos em `rounded-md`.
|
|
8
13
|
- Reprova: criar uma casca `bg-card` que herda o texto global ou introduzir `rounded-widget`
|
|
9
14
|
quando a escala `rounded-*` atende ao papel.
|
|
10
15
|
- Reprova: trocar automaticamente o radius de um controle quando apenas seu tamanho muda ou
|
|
11
16
|
manter cantos arredondados nas arestas internas de um grupo conectado.
|
|
17
|
+
- Reprova: reconstruir manualmente o container de página, usar `Empty` tracejado como vazio de
|
|
18
|
+
tabela, alinhar toda coluna numérica à direita por inferência ou destacar “Cancelar” como ação
|
|
19
|
+
primária em modal.
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
- Campos, labels, mensagens e invalidações pertencem ao contrato quando são parte da
|
|
5
5
|
operação, não a uma tela isolada.
|
|
6
6
|
- URL representa estado que precisa sobreviver a refresh, deep link ou histórico.
|
|
7
|
+
- `Page` fornece o `<main>`, o container centralizado com teto padrão de `72rem` e o header
|
|
8
|
+
de página. Alterar `className` apenas quando a superfície tiver uma necessidade real de
|
|
9
|
+
largura; não reconstruir esse container em cada rota.
|
|
10
|
+
- `ContentHeader` compartilha a composição de título, descrição, metadados e ações entre
|
|
11
|
+
páginas e seções. `variant` define o destaque visual; `level` preserva separadamente a
|
|
12
|
+
hierarquia semântica do heading.
|
|
7
13
|
- Componentes compartilhados não impõem margem externa; páginas e shells compõem layout.
|
|
8
14
|
- A fonte raiz pertence ao navegador e à aplicação. Medidas escaláveis usam `rem` ou a escala
|
|
9
15
|
relativa do Tailwind; `px` fica restrito a hairlines e compensações ligadas a essas bordas.
|
|
@@ -16,4 +22,24 @@
|
|
|
16
22
|
- Superfície aninhada evita moldura dupla e normalmente usa um degrau abaixo do contêiner.
|
|
17
23
|
Grupos conectados removem os raios internos. O tamanho do controle não muda sua forma;
|
|
18
24
|
`shape="pill"` declara a pílula quando ela for intencional.
|
|
25
|
+
- `ActionFilterBar` renderiza busca, filtros, período e ações declarados por uma list action
|
|
26
|
+
quando a tela precisa da toolbar sem entregar os resultados a `ActionList`. Manter seu
|
|
27
|
+
`state` e `onStateChange` ligados à mesma projeção de URL usada pela superfície.
|
|
28
|
+
- `ActionForm` mantém validação, execução e estados do contrato nos dois modos: sem `children`,
|
|
29
|
+
renderiza os campos declarados; com `children`, o consumidor diagrama `ActionFormField` e
|
|
30
|
+
controles customizados pelo contexto. `ActionFormCard` e `ActionFormDialog` acrescentam a
|
|
31
|
+
casca; não duplicar o form para obter card ou modal. Cancelamento em forms, confirmações e
|
|
32
|
+
modais usa `ghost`, deixando o destaque visual para a ação principal.
|
|
33
|
+
- `ActionList` mantém fetch, toolbar, loading, erro, retry, vazio, seleção e paginação enquanto
|
|
34
|
+
permite três composições de resultado: tabela por `columns`, renderer completo por `children`
|
|
35
|
+
ou views nomeadas. Usar `ActionFilterBar` isoladamente só quando outra superfície assumir a
|
|
36
|
+
renderização dos resultados. Colunas numéricas não recebem alinhamento ou tipografia por
|
|
37
|
+
inferência do tipo: declarar `className` no header e na célula quando o produto exigir, de
|
|
38
|
+
modo que números preservem o alinhamento natural por padrão.
|
|
39
|
+
- `ItemGroup` representa uma coleção e pode oferecer moldura explícita; `Item` já carrega
|
|
40
|
+
semântica de item de lista. Preferir essa composição a uma sequência visual sem estrutura.
|
|
41
|
+
- `Empty` com moldura tracejada comunica uma região disponível para criar ou vincular algo.
|
|
42
|
+
Quando uma tabela, card ou grupo existente apenas não tem resultados, manter sua moldura
|
|
43
|
+
sólida e renderizar o vazio estrutural dentro dela; não trocar toda ausência de dados por
|
|
44
|
+
`Empty`.
|
|
19
45
|
- Catálogo e API efetivos vêm dos exports da versão instalada, não de memória ou exemplo antigo.
|