@nexdom/uimed-vue 2.0.0-beta.3 → 2.0.0-beta.31

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/AGENTS.md CHANGED
@@ -22,15 +22,15 @@ pnpm add @nexdom/uimed-vue
22
22
  yarn add @nexdom/uimed-vue
23
23
  ```
24
24
 
25
- Peer dependencies (`@fontsource/roboto`, `@mdi/font`, `@nexdom/shared`, `resize-observer-polyfill`, `vite-plugin-vuetify`, `vite-plus`, `vue`, `vue-router`, `vuetify`) must be installed alongside it — most package managers install these automatically, but confirm versions match what's declared in `package.json`'s `peerDependencies`.
25
+ Peer dependencies (`@mdi/font`, `@nexdom/shared`, `resize-observer-polyfill`, `sass-embedded`, `vite-plus`, `vue`, `vue-component-type-helpers`, `vue-router`, `vuetify`) must be installed alongside it — most package managers install these automatically, but confirm versions match what's declared in `package.json`'s `peerDependencies`.
26
26
 
27
27
  ### Setup
28
28
 
29
- Add the Vite plugin:
29
+ Add the Vite plugin, which compiles the components' styles with the library's font and Sass settings, and loads the icon font with `font-display`, so it doesn't block rendering while it loads. Without it, components lose the Unimed Slab font:
30
30
 
31
31
  ```ts
32
32
  // vite.config.ts
33
- import { vitePluginUimed } from "@nexdom/uimed-vue/plugins.ts";
33
+ import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
34
34
 
35
35
  // ...
36
36
 
@@ -55,18 +55,18 @@ createApp(App).use(uimed).mount("#app");
55
55
 
56
56
  ### Usage
57
57
 
58
- Place the `Root` component at the top of `App.vue`, then add other components as needed:
58
+ Place the `UMain` component at the top of `App.vue`, then add other components as needed:
59
59
 
60
60
  ```vue
61
61
  <!-- App.vue -->
62
62
  <template>
63
- <root>
63
+ <u-main>
64
64
  <!-- ... -->
65
- </root>
65
+ </u-main>
66
66
  </template>
67
67
 
68
68
  <script setup lang="ts">
69
- import { Root } from "@nexdom/uimed-vue/components";
69
+ import { UMain } from "@nexdom/uimed-vue/components";
70
70
  </script>
71
71
  ```
72
72
 
@@ -102,23 +102,87 @@ vp env setup # create shims like vpr and vpx
102
102
  vp install # install dependencies
103
103
  ```
104
104
 
105
+ On Windows, prefer VSCode's "Dev Containers: Clone Repository in Container Volume" over opening a folder cloned on the host:
106
+
107
+ - A host clone with `core.autocrlf=true` checks files out with CRLF, and `vpr check` then reports formatting issues on every file. `.gitattributes` enforces LF, but clones made before it was added need `git rm -rq --cached . && git reset --hard` to be re-normalized (commit or stash local changes first, since `reset --hard` discards them).
108
+ - A host folder bind-mounted into the container is roughly 10x slower (unit tests alone go from seconds to minutes), which makes Stryker's initial test run time out.
109
+
105
110
  ## Commands
106
111
 
107
- - `vpr check` — lint, formatter, and type-check (requires `build`/`pack` to have run first for the type-check step)
112
+ - `vpr check` — lint, formatter, and type-check (builds the library first, since the type-check reads `dist`)
108
113
  - `vp test --coverage` — unit tests with coverage (Vitest, jsdom, 100% coverage threshold enforced)
109
- - `vpr test:mutations` — mutation tests (Stryker; thresholds: high 100, low 95, break 95)
110
- - `vpr test:e2e` — E2E tests (Playwright, runs against the built docs preview site)
114
+ - `vpr test:mutations` — mutation tests (Stryker; thresholds: high 100, low 100, break 100). It starts one test runner per CPU core; on machines with limited memory, run `vpx stryker run --concurrency 4` instead
115
+ - `vpr test:e2e` — E2E tests (Playwright, runs against the built docs preview site). Outside CI it reuses any server already on port 4173, which is also `vpr docs:dev`'s port: stop the dev server first, or the tests run against it instead of the built site. A running `vitepress preview` keeps serving the build it started with, so restart it after rebuilding the docs
111
116
  - `vpr depcruise` — architecture/dependency rules (dependency-cruiser)
112
117
  - `vp pack` / `vpr build` — build the library
113
118
  - `vpr docs` / `vpr docs:dev` — run docs site (imports the lib from `dist`, not `src` — run `vpr dev` in a second terminal to keep `dist` updated while iterating)
114
119
 
115
- CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pack`, `vpr check`, `vpr depcruise`, `vp test --coverage`, `vpr test:mutations`, `vpr test:e2e`. Match this locally before opening a PR.
120
+ CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pack`, `vpr check`, `vpr depcruise`, `vp test --coverage`, `vpr test:mutations`, `vpr test:e2e`, and then the SonarQube analysis (`vpr sonar`), whose quality gate fails the PR on any issue or unreviewed security hotspot in `src/` (see [Static analysis](#static-analysis-sonarqube)). Match this locally before opening a PR.
121
+
122
+ ### Focused runs while iterating
123
+
124
+ The full suite takes minutes; while working on a single component or composable, scope each step to it (`button` below is an example) and run the full suite only before opening a PR:
125
+
126
+ ```bash
127
+ # Unit tests of one or more folders
128
+ vp test src/components/button
129
+
130
+ # Same, with coverage limited to the files you changed. Without `--coverage.include`, files that
131
+ # are only imported (e.g. the children of `main.vue`) count as uncovered and fail the 100% threshold
132
+ vp test src/components/button --coverage --coverage.include="src/components/button/**"
133
+
134
+ # Mutation tests of specific files. List several files in a single comma-separated `--mutate`
135
+ # (repeating the flag keeps only the last one)
136
+ vpx stryker run --mutate "src/components/button/button.vue,src/composables/button/button.ts"
137
+
138
+ # One E2E spec
139
+ vpr test:e2e e2e/components/button.spec.ts
140
+ ```
141
+
142
+ ### Static analysis (SonarQube)
143
+
144
+ The company's SonarQube runs only in CI, after the E2E tests. Write code that avoids the rules that have already failed PRs here: use `globalThis` instead of `window`; move inner functions that don't use anything from the outer function out of it (S7721); don't use deprecated APIs (e.g. `event.returnValue`); don't write template comments that look like code; don't add non-null assertions or casts that don't change the type (S4325); no regular expressions with super-linear backtracking, such as two overlapping quantifiers (S5852); and prefer native elements to `role` attributes (S6819).
145
+
146
+ Only if CI's SonarQube step fails and its dashboard doesn't show why, reproduce the analysis with a local SonarQube Community in the server's version (`sonarqube:26.3.0.120487-community`) and `vpx @sonar/scan` pointed at it. The goal is 0 issues and no new security hotspots in the files the PR changed.
147
+
148
+ ## Adding a new component
149
+
150
+ Use an existing component (e.g. `button`) as the reference, and deliver all of the following in the same PR:
151
+
152
+ 1. `src/components/<name>/<name>.vue` (following the pattern in [Code conventions](#code-conventions)), `types.ts` and `__tests__/<name>.test.ts`.
153
+ 2. The `U`-prefixed export in `src/components/index.ts`.
154
+ 3. A usage guide at `docs/guide/components/<name>.md`, with `<demo>` examples (and a `<playground>` section when the component has configurable props), and an API reference at `docs/api/components/<name>.md` (props, events and slots tables). Both in Portuguese. In a demo's template inside a `.md` file, don't put an attribute with a template literal after one with an arrow function in the same tag: the Markdown type-check (`vue-tsc`) reads the `>` of `=>` as the end of the tag and fails with `TS1003`. Order the attributes so the template literal comes first.
155
+ 4. Both pages registered in the sidebar at `docs/.vitepress/config.ts`.
156
+ 5. `e2e/components/<name>.spec.ts` covering the guide's interactive examples and a "UI consistency" screenshot check (plus accessible snapshots where relevant). Commit the generated files under `__snapshots__`/`__screenshot__`.
157
+ 6. The full CI sequence passing locally.
158
+
159
+ ## Adding a new composable
160
+
161
+ Use an existing composable (e.g. `use-toast`) as the reference, and deliver in the same PR:
162
+
163
+ 1. `src/composables/<group>/<name>.ts` and `__tests__/<name>.test.ts`, plus the types in the group's `types.ts`. If it relies on an internal component (as `useToast` relies on the internal `Toast` rendered by `UMain`), that component follows the component layout but isn't exported.
164
+ 2. The export in `src/composables/index.ts`, and its assertion in `src/composables/__tests__/index.test.ts`.
165
+ 3. A usage guide at `docs/guide/composables/<name>.md` and an API reference at `docs/api/composables/<name>.md`, both in Portuguese, registered in both sidebars at `docs/.vitepress/config.ts` and listed in `docs/api/index.md` and in the table of `docs/guide/index.md`. If it relies on `UMain` (like `useToast`), also mention it in `UMain`'s guide and API pages.
166
+ 4. `e2e/composables/<name>.spec.ts` covering the guide's examples and a "UI consistency" screenshot check.
167
+ 5. The full CI sequence passing locally.
116
168
 
117
169
  ## Code conventions
118
170
 
119
171
  - All `src` code is written in English. `docs` content is written in Portuguese (aimed at Brazilian users), even though file/dir names stay in English.
120
172
  - `docs` must not mention Vuetify
121
- - Never write CSS, classes or any kind of styling. Always use component props.
173
+ - A new component/composable must be listed in its guide page (`docs/guide/…`), its API page (`docs/api/…`), both sidebars in `docs/.vitepress/config.ts`, the guide index table (`docs/guide/index.md`) and the API index list (`docs/api/index.md`).
174
+ - Never write CSS or `<style>` blocks, and expose styling to consumers only through props.
175
+ - Adjustments that Vuetify exposes as Sass variables (e.g. a font family or a line height) go in `src/styles/settings.scss`, which `vitePluginUimed()` compiles into Vuetify's stylesheets in the apps. That file only configures variables: still no CSS rules.
176
+ - Don't add spacing to Vuetify components: no padding or margin utility classes (`pa-*`, `pt-6`, `px-4`, `ma-2`, ...) and no spacing props overriding the defaults. Vuetify already applies Material Design 3 spacing, and overriding it breaks what it handles on its own, such as button paddings and the scrollbar of scrollable content. If the default spacing looks wrong, stop and ask instead of adjusting it. The one accepted layout margin is `mt-auto` on `v-card-actions` (as in `USection`), which pushes the actions to the bottom of a full-height card without setting any spacing value.
177
+ - Prefer a Vuetify component's own props to building its parts by hand (e.g. `title`/`text` on `v-card` instead of `v-card-item` > `v-card-title` > `v-card-text`). A non-spacing utility class is acceptable only when no prop covers the need (e.g. `text-wrap` on a card title), with a comment saying why. Props covered by the shared composables below are the exception: they always go through them.
178
+ - Reuse the shared internal composables for props that recur across components, so every component maps them to Vuetify the same way, instead of mapping them again:
179
+ - `fullWidth`/`fullHeight`: `useFullWidth`/`useFullHeight` (`src/composables/dimensions/dimensions.ts`), applying Vuetify's `w-100`/`h-100` classes even where the component has `width`/`height` props.
180
+ - `alignX`/`alignY`: `useAlignX`/`useAlignY` (`src/composables/alignment/alignment.ts`), with the `AlignX`/`AlignY` types from the group's `types.ts`.
181
+ - `color`: `useVuetifyColor` (`src/composables/colors/use-vuetify-color.ts`), with the `ColorVariant` type from the group's `types.ts`.
182
+
183
+ A new prop that does the same as one of these takes its name and type. When a second component needs a mapping that only one component has, move it into a shared composable first.
184
+
185
+ - Keep components thin: a component wires props, slots and events to Vuetify. Logic beyond that (focus management, queues, DOM lookups, comparisons) goes into an internal composable with its own unit tests.
122
186
  - Path aliases: `@/*` → `src/*`, `@e2e/*` → `e2e/*`.
123
187
  - Every public component follows this pattern to block access to internals and give it an editor-hover description:
124
188
 
@@ -138,8 +202,10 @@ CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pa
138
202
  </script>
139
203
  ```
140
204
 
141
- - Component layout: `src/components/<name>/<name>.vue`, `types.ts` for prop/option types, `__tests__/<name>.test.ts` for unit tests. Composables follow the same shape under `src/composables/<name>/`.
142
- - Public components/composables/types are re-exported from `src/components/index.ts` and `src/composables/index.ts`.
205
+ - Component layout: `src/components/<name>/<name>.vue`, `types.ts` for prop/option types, `__tests__/<name>.test.ts` for unit tests. Components that belong to a group live in the group's folder (e.g. the internal `src/components/dialogs/toast.vue`).
206
+ - Composables live in a group folder: `src/composables/<group>/<name>.ts`, with `types.ts` and `__tests__/` in the group folder (e.g. `src/composables/dialogs/use-toast.ts`).
207
+ - Library-wide configuration constants that aren't components or composables live in `src/consts/<name>.ts` (e.g. `src/consts/locale.ts`, the language set by `createUimed()` and `vueTestUtilsPluginUimed()`).
208
+ - Public components/composables are re-exported from `src/components/index.ts` and `src/composables/index.ts`. Prop types stay internal (`types.ts` is not re-exported); consumers derive them with `ComponentProps` (see [Usage](#usage)).
143
209
  - Every publicly exported component must use the `U` prefix on its export identifier (e.g., `UButton`, `UMain`), while internal file names and component names remain unprefixed.
144
210
  - Use [JSDoc](https://jsdoc.app/about-getting-started) on every method/prop/type intended to be part of the public API — it's the primary documentation surface and supports markdown/code examples.
145
211
  - Known workarounds (see CONTRIBUTING.md before touching related config): `stryker-vue-ignorer` patches a Stryker/Vue macro-hoisting issue; `vue-tsc` is used for type-check instead of Vite+'s built-in one due to an oxlint/Vue support gap. Both are meant to be removed once their upstream issues are fixed — don't build further on top of them without checking if they're still needed.
@@ -148,8 +214,48 @@ CI (`.github/workflows/ci.yml`) runs, in order: commitlint on PR commits, `vp pa
148
214
 
149
215
  - Unit tests use Vitest + `@vue/test-utils`, with `vueTestUtilsPluginUimed()` from `@/unit-test.ts` to mount a Vuetify instance.
150
216
  - Global unit test setup (`src/__tests__/setup.ts`) stubs `visualViewport`, uses fake timers, and silences `console.error/warn/log`.
151
- - Coverage threshold is 100%; mutation testing threshold is 100% (break at 100). Don't add code paths without covering tests.
217
+ - With fake timers in jsdom, Vuetify transitions (e.g. of `VDialog`, `VMenu`) don't finish on their own. Emit the transition events on the Vuetify component (`wrapper.findComponent(VDialog).vm.$emit("afterLeave")`) or advance the timers with `await vi.runAllTimersAsync()`.
218
+ - Coverage threshold is 100%; mutation testing threshold is 100% (break at 100). Don't add code paths without covering tests. Mutants that don't compile are reported as compile errors and don't count toward the score.
152
219
  - E2E tests (Playwright, `e2e/`) run against the built docs preview (`http://localhost:4173/uimed-vue/`). Snapshots/screenshots live under `__snapshots__`/`__screenshot__` next to each spec.
220
+ - Before keeping a new screenshot as a baseline, open it and check it against how plain Vuetify renders the same component: overflowing content shows its scrollbar, buttons and cards keep their native paddings, nothing touches the edges or gets clipped. A screenshot that only "looks fine" isn't enough: name what you checked in your report.
221
+ - Generate and update screenshots only on Linux, as CI does: in the Dev Container, or in the Playwright Docker image of the version in `package.json` (`mcr.microsoft.com/playwright:v<version>-noble`). Screenshots taken on Windows or macOS render fonts differently and fail in CI.
222
+ - Update baselines with `--update-snapshots=changed`, scoped to the spec you changed. `--update-snapshots=all` also rewrites accessible snapshots (`.aria.yml`) that didn't need to change.
223
+ - Keep temporary files (scripts, specs, configs) outside the repo, e.g. in the system's temp folder. Vitest collects test files from every folder except the excluded ones in `vite.config.ts`, so a temporary spec in a gitignored folder such as `reports/` still runs, and fails, with the unit tests.
224
+ - Never run Stryker at the same time as another test command: they share build output and Stryker fails with `ENOENT`.
225
+ - Unit tests that mount several apps at once (e.g. nested dialogs) need a distinct `global.config.idPrefix` per mount, since `useId()` restarts in each app.
226
+ - In E2E, a paused `page.clock` also freezes transitions, so overlays never finish leaving. Use `pauseClock(page)` from `e2e/utils.ts` to hold time-based work (e.g. a demo action with `setTimeout`), `page.clock.fastForward` to complete it, then `page.clock.resume()` before expecting the overlay to be hidden (see `e2e/composables/use-confirm.spec.ts`). Don't call `page.clock.pauseAt` with the test runner's time (`new Date()`, `Date.now()`): the page clock can be ahead of it, and the pause then fails with "Cannot fast-forward to the past". `pauseClock` pauses from the page's own time instead.
227
+
228
+ ## Public API design
229
+
230
+ The public API is hard to change once released, and every prop, event or option is something consumers can misuse and maintainers must keep. Keep it minimal:
231
+
232
+ - Only add what the issue asks for. Anything beyond it (an extra prop, event, option or variant) is a separate decision the maintainers must approve explicitly, not a default.
233
+ - Behaviors meant to be consistent across the library aren't per-instance options. For example, every dialog closes with `Esc`, a click outside it or the browser's back button; a flag to block that stays internal.
234
+ - Reuse existing contracts before creating new ones: `v-model` for open/closed state instead of extra lifecycle events such as `afterLeave`, the `actions` shape of `USection` for buttons, the option names of existing composables.
235
+
236
+ ## Library documentation and dependencies
237
+
238
+ The stack is newer than most AI models' training data (Vue 3.5, Vuetify 4, Vue Router 5, Vite+ 0.2, Vitest 4, TypeScript 6, VitePress 2 alpha, Stryker 10, Playwright 1.62), so don't rely on memory for library APIs.
239
+
240
+ - Before using an API this repo doesn't use yet, or implementing something from scratch, look it up in the version declared in `package.json`. The project's `.mcp.json` provides two servers for that:
241
+ - `context7`, with these library IDs: Vue `/websites/vuejs`, Vuetify `/websites/vuetifyjs_en`, Vue Router `/websites/router_vuejs`, Vite+ `/websites/viteplus_dev`, Vitest `/vitest-dev/vitest`, Vue Test Utils `/vuejs/test-utils`, Playwright `/microsoft/playwright`, VitePress `/vuejs/vitepress`, Stryker `/stryker-mutator/stryker-js`.
242
+ - `vuetify`, Vuetify's own server, for component and composable APIs and release notes. Its tools that create or update bins, links, playgrounds or bug reports publish content outside the repo and are denied in `.claude/settings.json`.
243
+ - Queries to both servers leave your machine: describe what you need in generic terms and never include source code or business rules.
244
+ - Before adding a dependency, check whether Vuetify, `@nexdom/shared` or the current dependencies already cover the need. If not, confirm with the requester, check the package's docs, maintenance and license, and declare runtime dependencies as `peerDependencies` (enforced by dependency-cruiser's `use-peer-deps` rule).
245
+ - Vuetify is an internal detail: consumers use the library's props, and `docs` never mention Vuetify.
246
+
247
+ ## AI agents
248
+
249
+ Besides this file, the repo ships Claude Code subagents in `.claude/agents/`:
250
+
251
+ - `issue-planner`: turns an issue into an API proposal plus open questions, before any code is written.
252
+ - `issue-implementer`: implements an issue end to end (source, tests, docs, E2E).
253
+ - `code-reviewer`: reviews a branch or PR against these conventions, without changing code.
254
+ - `dependency-updater`: evaluates and applies dependency updates, such as Dependabot PRs.
255
+
256
+ Issues and PRs live in `nexdom-healthtech/uimed-vue`. Pass `-R nexdom-healthtech/uimed-vue` to `gh`, since clones from forks have issues disabled, and read issues with `gh issue view <number> -R nexdom-healthtech/uimed-vue --json title,body,comments` (`--comments` prints nothing in non-interactive shells).
257
+
258
+ To resolve an issue, run `/resolve-issue <number>` (`.claude/skills/resolve-issue/`). It chains planner, implementer and reviewer, asks you about open questions and waits for your approval of the public API before any code is written.
153
259
 
154
260
  ## Git workflow
155
261
 
package/README.md CHANGED
@@ -41,7 +41,7 @@ Add Vite config:
41
41
 
42
42
  ```ts
43
43
  // vite.config.ts
44
- import { vitePluginUimed } from "@nexdom/uimed-vue/plugins.ts";
44
+ import { vitePluginUimed } from "@nexdom/uimed-vue/plugins";
45
45
 
46
46
  /// ...
47
47