nuxt-unified-ui 0.5.16 → 0.5.17

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/README.md CHANGED
@@ -20,7 +20,7 @@ vpr serve
20
20
 
21
21
  ## Agent Skills
22
22
 
23
- One installable Agent Skill lives under `skills/nuxt-unified-ui/` (`npx skills` compatible). It covers the layer API **and** mandatory Nuxt code style (forms, dialogs, radashi, formatting).
23
+ One installable Agent Skill lives under `skills/nuxt-unified-ui/` (`npx skills` compatible). It covers the layer API **and** mandatory Nuxt code style (forms, dialogs, radashi, formatting). When an agent finishes implementing work, it runs `/nuxt-unified-ui apply`: one subagent per `.vue`, `.ts`, or `.js` file applies the skill's logical, structural, and code-style rules — to every file on `dev` / `main` / `master`, or to the current branch's changed files elsewhere. You can also invoke `/nuxt-unified-ui apply` directly.
24
24
 
25
25
  ```bash
26
26
  npx skills add . --list
@@ -134,15 +134,34 @@ function getExtraActionItems(row) {
134
134
  'href',
135
135
  'disabled',
136
136
  'onClick',
137
+ 'onSelect',
137
138
  ]),
138
139
  href: resolveActionValue(it.href, row),
139
140
  to: resolveActionValue(it.to, row),
140
141
  disabled: resolveActionValue(it.disabled, row),
141
- onClick: () => it.onClick?.(row.original),
142
+ onSelect: () => it.onClick?.(row.original),
142
143
  };
143
144
  });
144
145
  }
145
146
 
147
+ function handleRowSelect(_event, row) {
148
+
149
+ if (!props.rowTo) {
150
+ return;
151
+ }
152
+
153
+
154
+ const destination = props.rowTo(row.original);
155
+
156
+ if (!destination) {
157
+ return;
158
+ }
159
+
160
+
161
+ return navigateTo(destination);
162
+
163
+ }
164
+
146
165
 
147
166
  /* pagination */
148
167
 
@@ -174,7 +193,8 @@ const pageSizeItems = computed(() => {
174
193
  :data="props.data || []"
175
194
  :column-pinning="columnPinning"
176
195
  :ui="tableUi"
177
- :meta="props.meta">
196
+ :meta="props.meta"
197
+ @select="props.rowTo ? handleRowSelect : undefined">
178
198
 
179
199
  <template v-for="(_, name) in $slots" #[name]="slotData">
180
200
  <slot
@@ -11,8 +11,15 @@
11
11
  * @example
12
12
  * unSet({}, 'cards[0].value', 2)
13
13
  * // => { cards: [{ value: 2 }] }
14
+ *
15
+ * Paths that contain `__proto__`, `prototype`, or `constructor` are ignored.
14
16
  */
15
17
 
18
+ function isUnsafePathSegment(segment: string) {
19
+ return segment === '__proto__' || segment === 'prototype' || segment === 'constructor';
20
+ }
21
+
22
+
16
23
  export function unSet(target: any, key: string, value: any) {
17
24
 
18
25
  const segments = (
@@ -22,7 +29,7 @@ export function unSet(target: any, key: string, value: any) {
22
29
  .filter(Boolean)
23
30
  );
24
31
 
25
- if (segments.length === 0) {
32
+ if (segments.length === 0 || segments.some(isUnsafePathSegment)) {
26
33
  return target;
27
34
  }
28
35
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-unified-ui",
3
3
  "type": "module",
4
- "version": "0.5.16",
4
+ "version": "0.5.17",
5
5
  "main": "./nuxt.config.ts",
6
6
  "types": "./index.d.ts",
7
7
  "exports": {
@@ -20,9 +20,9 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "@formkit/tempo": "1.1.0",
23
- "@iconify-json/lucide": "1.2.136",
23
+ "@iconify-json/lucide": "1.2.138",
24
24
  "@nuxt/kit": "4.5.2",
25
- "@nuxt/ui": "4.11.2",
25
+ "@nuxt/ui": "4.11.3",
26
26
  "@nuxtjs/i18n": "10.6.0",
27
27
  "@types/canvas-confetti": "1.9.0",
28
28
  "@vueuse/core": "15.0.0",
@@ -1,181 +1,122 @@
1
1
  ---
2
2
  name: nuxt-unified-ui
3
3
  description: >-
4
- Single skill for the nuxt-unified-ui Nuxt layer and mandatory Nuxt code style:
5
- install/extend the layer, required CSS, modules/config, radashi radXxx
6
- auto-imports, un-form / useForm, launchFormPickerDialog /
7
- launchChoicePickerDialog, toast helpers, un-card / un-typography / un-table
8
- (attribute order, row actions, pagination), pages /
9
- definePageMeta, page /* params */ and /* seo */ blocks, ufetch / useUFetch wrapping, unified resources
10
- (server plugins, REST handleResource*, resource-manager dashboard /
11
- customization), layer-private app/atoms and app/libs vs public
12
- components/utils (relative imports), attribute order/defaults, and
13
- whitespace/formatting conventions for all Nuxt-generated code. Use when
14
- working in or consuming nuxt-unified-ui, declaring or customizing resources,
15
- or whenever generating Vue/Nuxt code that must match unified code style.
4
+ Builds and edits Nuxt apps and layers that use nuxt-unified-ui: un-form /
5
+ useForm schema forms, un-card, un-table, launchFormPickerDialog /
6
+ launchChoicePickerDialog / launchDialog, toast helpers, radXxx radashi
7
+ auto-imports, and the companion unified stack (ufetch, useUFetch,
8
+ resource-manager, createUnifiedResourceController). Use when package.json
9
+ depends on nuxt-unified-ui, nuxt.config extends it, or code uses these
10
+ APIs. Whenever implementation work is finished, run `/nuxt-unified-ui
11
+ apply`, which applies the skill file by file to the whole project (on dev,
12
+ main, or master) or to the current branch's changed files; users can also
13
+ invoke it directly.
16
14
  ---
17
15
 
18
16
  # nuxt-unified-ui
19
17
 
20
- Reusable **Nuxt layer** (Nuxt UI + helpers) **plus** the mandatory **code style** for Nuxt projects using this stack.
21
-
22
- Peer dependency: **Nuxt `>=4.5.2`**.
23
-
24
- This is the **only** installable skill in this repo. Deep topics live under `references/`.
25
-
26
- ## When to use
27
-
28
- - Installing / extending `nuxt-unified-ui` as a Nuxt layer
29
- - Using `un-form`, dialogs, toasts, `un-card`, `un-table`, radashi `radXxx`, etc.
30
- - Declaring or customizing **unified resources** (plugins, REST, dashboard)
31
- - **Whenever generating or editing Nuxt/Vue/server code** that must follow the unified look (whitespace, wrapping, template shape, sectioning)
32
-
33
- ## References (read as needed)
34
-
35
- | Topic | File |
36
- |-------|------|
37
- | **Code style (mandatory)** | [references/code-style.md](references/code-style.md) |
38
- | Unified resources (plugins → REST → dashboard) | [references/resources.md](references/resources.md) |
39
- | Pages / routing | [references/pages.md](references/pages.md) |
40
- | Data fetching (`ufetch` / `useUFetch`) | [references/data-fetching.md](references/data-fetching.md) |
41
- | Layer install + required CSS | [references/layer-setup.md](references/layer-setup.md) |
42
- | Public surface inventory | [references/public-surface.md](references/public-surface.md) |
43
- | Forms (`useForm` / `un-form`) | [references/forms.md](references/forms.md) |
44
- | Form field schema | [references/form-field-schema.md](references/form-field-schema.md) |
45
- | Form elements | [references/form-elements.md](references/form-elements.md) |
46
- | Dialogs / toasts / UI | [references/dialogs.md](references/dialogs.md) |
47
- | Dialog implementation | [references/dialogs-impl.md](references/dialogs-impl.md) |
48
- | Toast + `un-*` details | [references/toast-and-ui.md](references/toast-and-ui.md) |
49
- | Tables (`un-table`) | [references/tables.md](references/tables.md) |
50
- | Radashi `radXxx` catalog | [references/radashi.md](references/radashi.md) |
51
-
52
- ---
53
-
54
- ## Code style (read [code-style.md](references/code-style.md) before writing code)
55
-
56
- **Always apply** to Vue SFCs and app/server `.ts` files. Higher-level idea: code should **scan vertically** — named sections, declaration-kind groups, multi-line literals, and predictable template wrapping.
57
-
58
- Absolute highlights:
59
-
60
- - `<script setup>` only — **never** `lang="ts"`; no TS annotations in Vue (runtime prop types)
61
- - 2-space indent; single quotes; semicolons; trailing commas in multi-line literals
62
- - **`.js` / `.ts` file start:** two leading blank lines, **except** when the file starts with imports — then **no** blank lines before the first `import`
63
- - Every `<script setup>` section starts with `/* section name */`, then a blank line
64
- - Within a section, group declarations by kind (imports, refs, computeds, watchers, functions, etc.): two blank lines between groups; no blanks between consecutive refs; one blank between consecutive members of other groups
65
- - Non-trivial async/functions: blank line after `{`, double blank between major steps, blank before `}`
66
- - A function whose body is one `for` / `while` / `if` or one connected `if` / `else` / `else if` or `try` / `catch` / `finally` chain stays flush: no blank lines between the function `{` / `}` and that block
67
- - A function dedicated to choosing a return value from multiple criteria uses one exhaustive `if` / `else if` / `else` chain; broader functions may use guard clauses and early returns
68
- - `else` / `catch` on their own line after `}`
69
- - Script object literals always multi-line (even one property)
70
- - Kebab-case tags (`u-button`, `un-card`)
71
- - `v-if` / `v-for` on `<template>` wrappers — not on rendered nodes
72
- - If a condition changes several component attributes, prefer explicit `<template v-if>` / `v-else` component variants over nested ternaries and overly dynamic bindings
73
- - Tags with children keep all attributes on one line with the opening tag unless a multiline attribute (multi-line array/object/function binding) forces a split — then the opening tag goes on its own line, one attribute per line, value styled like JS; childless tags are self-closing — one single-line attribute stays inline, otherwise tag and attributes each go on their own line with `/>` on its own line; attribute order + default omissions (`variant="subtle"`, **Cancel only → `ghost`**, omit neutral `color`, `loading-auto`); split non-self-closing `>` on same line as last attr; split self-closing `/>` on its own line
74
- - `{{ ... }}` on its own line (static + dynamic text may mix)
75
- - `/* section */` comments; imports co-located under the section that uses them
76
- - Light naming: `handleXxx` handlers, `it` in short callbacks, descriptive `for...of`, computeds use block + `return`
77
- - Pages: explicit `definePageMeta.name`, `/* params */` for `route.params` / `route.query`, required `/* seo */` (`useHead` + `useSeoMeta`, `useJsonld` when installed) → [pages.md](references/pages.md)
78
- - Fetching: `ufetch(url, {` one line; `useUFetch` with URL on next line + `data*Data` / `is*Loading` / `refresh*` → [data-fetching.md](references/data-fetching.md)
79
- - Layer modules: generate private first (`app/atoms/` components, `app/libs/` functions and similar); promote to `app/components/` / `app/utils/` only when another layer needs them; import `atoms`/`libs` with relative paths → [code-style.md](references/code-style.md)
80
-
81
- ---
82
-
83
- ## Quick start (host app)
84
-
85
- 1. Install the package.
86
- 2. Create host `assets/css/main.css`:
87
-
88
- ```css
89
- @import 'tailwindcss';
90
- @import '@nuxt/ui';
91
- @import 'nuxt-unified-ui/nuxt-ui-fixes.css';
92
- ```
93
-
94
- 3. Extend the layer (CSS wiring is **required**):
95
-
96
- ```js
97
- import { pathRelativeToBase } from 'nuxt-unified-ui';
98
-
99
- export default defineNuxtConfig({
100
- css: [
101
- pathRelativeToBase(import.meta.url, './assets/css/main.css'),
102
- ],
103
- extends: [
104
- 'nuxt-unified-ui',
105
- ],
106
- });
107
- ```
108
-
109
- 4. Wrap the app with `u-app`.
110
- 5. Prefer layer helpers (`useForm`, `launchFormPickerDialog`, `toastSuccess`) over reinventing them.
111
- 6. Generate all new code using [code-style.md](references/code-style.md).
112
-
113
- Details: [layer-setup.md](references/layer-setup.md).
114
-
115
- ## Package surface
116
-
117
- | Export | Path |
118
- |--------|------|
119
- | `nuxt-unified-ui` | `./nuxt.config.ts` (also re-exports `pathRelativeToBase`) |
120
- | `nuxt-unified-ui/app` | `./app` |
121
- | `nuxt-unified-ui/nuxt-ui-fixes.css` | `./app/assets/css/nuxt-ui-fixes.css` |
122
-
123
- Published: `nuxt.config.ts`, `index.d.ts`, `app/`, `i18n/`, `modules/`.
124
-
125
- ## Mental model (`app/`)
126
-
127
- | Path | Role |
128
- |------|------|
129
- | `app/components/` | `un-form`, `un-card`, `un-typography`, `un-spinner`, `un-table` |
130
- | `app/composables/` | `useForm`, `useFormExtraElements` |
131
- | `app/elements/` | Built-in form field renderers |
132
- | `app/dialogs/` | Form / choice picker modal UIs |
133
- | `app/utils/` | `launchDialog*`, `toast*`, `smartMatch`, `unSet`, dates, … |
134
- | `app/plugins/` | `$toaster` via `useToast()` |
135
- | `modules/radashi.ts` | Auto-imports radashi as `rad*` |
136
- | `i18n/locales/` | `en.json`, `de.json` |
137
-
138
- ## Layer config (inherited)
139
-
140
- From `nuxt.config.ts`: `@vueuse/nuxt`, `@nuxt/ui`, `@nuxtjs/i18n`; `ui.colorMode: false`; default variant `neutral`; i18n `no_prefix` with `en`/`de`; `experimental.typedPages: true`.
141
-
142
- ## Common tasks
143
-
144
- | Task | Prefer |
145
- |------|--------|
146
- | Schema form | `useForm` + `<form-tag />` / `<un-form>` → [forms.md](references/forms.md) |
147
- | Modal form | `launchFormPickerDialog` + `submitButton.onClick` → [dialogs.md](references/dialogs.md) |
148
- | Confirm / choice | `launchChoicePickerDialog` + button `onClick` (avoid `value`) |
149
- | Feedback | `toastSuccess` / `toastError` / `toastWarning` / `toastInfo` / `toast` |
150
- | Page chrome | `un-typography` + `un-card` |
151
- | Data table | `un-table` + parent-owned fetch / sort / filter → [tables.md](references/tables.md) |
152
- | Custom field | `registerFormExtraElement` in a plugin |
153
- | Utilities | `radXxx` → [radashi.md](references/radashi.md) |
154
- | New page / route | [pages.md](references/pages.md) |
155
- | List/detail fetch or mutation | [data-fetching.md](references/data-fetching.md) |
156
- | New / custom resource | [resources.md](references/resources.md) |
157
- | New component / util in a layer | Start in `app/atoms/` or `app/libs/`; relative imports; promote to `components/` / `utils/` only if another layer needs it → [code-style.md](references/code-style.md) |
158
- | Formatting any of the above | [code-style.md](references/code-style.md) |
159
-
160
- ## Do / don’t
161
-
162
- **Do**
163
-
164
- - Extend via `extends: ['nuxt-unified-ui']`
165
- - Keep required host `main.css` + `pathRelativeToBase` CSS entry + `nuxt-ui-fixes.css`
166
- - Use field `identifier` for element kind; `type` only for HTML input types
167
- - Handle dialog actions in `onClick`
168
- - Follow code style for every generated file
169
- - Resources: plugin → full REST set → dashboard nav / custom `<resource-manager>` page → [resources.md](references/resources.md)
170
- - Start generated layer components in `app/atoms/` and functions in `app/libs/`; import them with relative paths; move to `app/components/` / `app/utils/` only when another layer needs them
171
-
172
- **Don’t**
173
-
174
- - Invent APIs not in source
175
- - Reference any local playground as consumer docs
176
- - Use PascalCase component tags in templates
177
- - Set choice-button `value` unless the await result must distinguish buttons
178
- - Assume color mode is enabled (layer disables it)
179
- - Reimplement resource CRUD in route files (use `handleResource*`; customize via dedicated pages + domain APIs)
180
- - Put a layer-private component or util in `components/` or `utils/` (becomes app-global)
181
- - Import `atoms`/`libs` via `~/`, `#layers`, or from another layer — use relative paths; promote first if another layer needs them
18
+ The main agent decides what code exists, where it lives, and which APIs it calls. When the work is done, the apply pipeline runs one subagent per file to check the skill's rules and apply the code style; it is the only place formatting rules apply. The main agent does not load `references/code-style.md`.
19
+
20
+ ## Finishing work: `/nuxt-unified-ui apply`
21
+
22
+ Whenever you finish implementing work that added or edited a `.vue`, `.js`, or `.ts` file, run `/nuxt-unified-ui apply` before your final reply: follow [references/apply.md](references/apply.md). Do the same when the skill is invoked with the argument `apply`, or the user asks to apply nuxt-unified-ui across the project or the current branch. Invoked without an argument, the skill only loads its guidance for the task at hand.
23
+
24
+ On `dev`, `main`, or `master` it processes every `.vue`, `.js`, and `.ts` file; on any other branch, only the files that branch added or changed relative to its base branch. One subagent per file applies the logical, structural, and code-style rules, then the main agent carries out the cross-file follow-ups.
25
+
26
+ ## APIs
27
+
28
+ **Layer** APIs come from this layer and are auto-imported across the app and every layer. **Companion** APIs come from the companion unified layers, which real projects always include next to this one. When an API is not listed here, check the source before using it.
29
+
30
+ | API | Use it to | From | Reference |
31
+ |---|---|---|---|
32
+ | `un-form` / `useForm` | Render a schema-driven form; `useForm` returns `{ form, formTag }` | layer | [forms.md](references/forms.md) |
33
+ | `registerFormExtraElement` | Add a custom form element `identifier` (call from a Nuxt plugin) | layer | [forms.md](references/forms.md) |
34
+ | `useFormExtraElements` | Read the registered custom elements (add them with `registerFormExtraElement`) | layer | [forms.md](references/forms.md) |
35
+ | `un-card` | Card with icon / title / subtitle header, body, and action rows | layer | [toast-and-ui.md](references/toast-and-ui.md) |
36
+ | `un-typography` | Icon + title + subtitle + text block | layer | [toast-and-ui.md](references/toast-and-ui.md) |
37
+ | `un-spinner` | Show a loading spinner | layer | [toast-and-ui.md](references/toast-and-ui.md) |
38
+ | `un-table` | Show one page of rows with row actions and a pagination footer | layer | [tables.md](references/tables.md) |
39
+ | `launchFormPickerDialog` | Ask for form input in a modal; submit logic goes in `submitButton.onClick` | layer | [dialogs.md](references/dialogs.md) |
40
+ | `launchChoicePickerDialog` | Confirm or choose in a modal; logic goes in each button's `onClick` | layer | [dialogs.md](references/dialogs.md) |
41
+ | `launchDialog` | Open any dialog component; resolves with its `close` payload | layer | [dialogs.md](references/dialogs.md) |
42
+ | `toast`, `toastSuccess`, `toastError`, `toastWarning`, `toastInfo` | Show a toast (typed helpers set icon and color) | layer | [toast-and-ui.md](references/toast-and-ui.md) |
43
+ | `smartMatch` | Test a function, mongo-style filter, or truthy value against an object | layer | [forms.md](references/forms.md) |
44
+ | `unSet` | Set a nested path on an object, creating missing levels (mutates) | layer | — |
45
+ | `formatDate` / `parseDate` | Format and parse dates (`@formkit/tempo`) | layer | — |
46
+ | `isSlotFilled` | Check whether a slot has content | layer | — |
47
+ | `pathRelativeToBase` | Resolve a path against a file URL; also exported from the package for `nuxt.config` | layer | [layer-setup.md](references/layer-setup.md) |
48
+ | `makeConfetti` | Fire confetti: `template` picks a built-in effect (`parade`, `on-top` / `on-left` / `on-right` / `on-bottom`, `on-frame`, `split-on-top`, `on-curtain`), `amount` sets particles per burst, other args go to `canvas-confetti` | layer | — |
49
+ | `radXxx` | Any radashi function (`radGet`, `radPick`, …), in app and server | layer | [radashi.md](references/radashi.md) |
50
+ | `ufetch` | Make a one-off API request (submit, delete, click) | companion | [data-fetching.md](references/data-fetching.md) |
51
+ | `useUFetch` | Load reactive page data (`data`, `pending`, `refresh`) | companion | [data-fetching.md](references/data-fetching.md) |
52
+ | `parseSchema` | Compile a resource schema DSL into `{ schema, type, inferred }` | companion | [resources.md](references/resources.md) |
53
+ | `createUnifiedResourceController` / `UnifiedResourceController` | Create the typed Mongo controller (`dbo`) for a resource | companion | [resources.md](references/resources.md) |
54
+ | `app` / `UnifiedAppRegistry` | Global typed registry of resources (`app.users.dbo`) | companion | [resources.md](references/resources.md) |
55
+ | `handleResourceSchema`, `handleResourceList`, `handleResourceCreate`, `handleResourceCount`, `handleResourceRetrieve`, `handleResourceUpdate`, `handleResourceDelete` | Implement the standard REST routes of a resource | companion | [resources.md](references/resources.md) |
56
+ | `useResourceName` | Derive a resource's API path and display titles from its name | companion | [resources.md](references/resources.md) |
57
+ | `useResourceMeta` | Map a resource schema to form fields and table columns | companion | [resources.md](references/resources.md) |
58
+ | `<resource-manager>` | Full CRUD dashboard card for a resource; exposes `refreshResources()` | companion | [resources.md](references/resources.md) |
59
+ | `<resource-explorer-table>` | Filter bar + sortable headers + `un-table` for a resource | companion | [tables.md](references/tables.md) |
60
+ | `assertBody` | Validate a request body against a schema in a server route | companion | — |
61
+ | `assertRateLimit` | Rate-limit a server route | companion | — |
62
+ | `createUnauthenticatedError` | Throw a 401 from a server route | companion | — |
63
+ | `generateUuid` | Generate a UUID | companion | — |
64
+ | `useToken` | Read or set the auth token | companion | — |
65
+ | `is-authenticated` middleware; `dashboard` / `empty` layouts | Protect pages; dashboard and full-bleed page layouts | companion | [pages.md](references/pages.md) |
66
+ | `useJsonld` | Emit JSON-LD on public pages, only when `nuxt-jsonld` is installed | `nuxt-jsonld` | [pages.md](references/pages.md) |
67
+
68
+ ## File structure
69
+
70
+ The main agent decides where a file lives and which one job it has, and does it while implementing. Apply subagents only report misplaced files; they never move files or update callers.
71
+
72
+ Each `.vue`, `.js`, and `.ts` file has one responsibility. When a file would have two independent jobs, create two files.
73
+
74
+ Nuxt auto-imports `app/components/` and `app/utils/` across every layer. `app/composables/` is public in the same way. A file that must stay inside one layer does not go in those directories.
75
+
76
+ | Role | Directory | Visibility |
77
+ |---|---|---|
78
+ | Private Vue component | `app/atoms/` | This layer only |
79
+ | Private function, composable, or helper | `app/libs/` | This layer only |
80
+ | Public Vue component | `app/components/` | Whole app, auto-imported |
81
+ | Public util | `app/utils/` | Whole app, auto-imported |
82
+
83
+ Generate private first. New components start in `app/atoms/`. New functions and similar helpers start in `app/libs/`. Promote a file to `app/components/` or `app/utils/` only when another layer needs it. After a promote, update every caller and remove the relative import; public modules are auto-imported.
84
+
85
+ Import `atoms` and `libs` with relative paths only (`../atoms/foo.vue`, `../libs/bar`). Never use `~/`, `@/`, `#layers/`, or another alias. Never import another layer's `atoms` or `libs`; promote that file first, then use the public auto-import.
86
+
87
+ Do not register `atoms` or `libs` with Nuxt `components` or `imports` config.
88
+
89
+ ## Before writing
90
+
91
+ Read the reference that matches the task before creating or editing files. The details stay in that reference.
92
+
93
+ | Task | Read first |
94
+ |---|---|
95
+ | Layer installation and required CSS | [references/layer-setup.md](references/layer-setup.md) |
96
+ | Forms, field schema, built-in and custom elements | [references/forms.md](references/forms.md) |
97
+ | Dialogs | [references/dialogs.md](references/dialogs.md) |
98
+ | Toasts, `un-*` UI, button / badge / icon conventions | [references/toast-and-ui.md](references/toast-and-ui.md) |
99
+ | Tables | [references/tables.md](references/tables.md) |
100
+ | Pages and routing | [references/pages.md](references/pages.md) |
101
+ | `ufetch` / `useUFetch` | [references/data-fetching.md](references/data-fetching.md) |
102
+ | Unified resources | [references/resources.md](references/resources.md) |
103
+ | Radashi `radXxx` exports | [references/radashi.md](references/radashi.md) |
104
+
105
+ ## Decisions the main agent owns
106
+
107
+ Make these choices while implementing. The apply pipeline checks them again afterward.
108
+
109
+ - Prefer the APIs above over hand-rolled equivalents (raw `$fetch`, hand-built `u-modal` flows, direct `radashi` imports).
110
+ - A new resource includes its server plugin, the full REST route set, and a dashboard nav entry or a custom `<resource-manager>` page.
111
+ - Use `ufetch` for a one-off request. Use `useUFetch` for reactive page data.
112
+ - Every page has an explicit `definePageMeta.name` and sets SEO with `useHead` (title) and `useSeoMeta` (description).
113
+ - Buttons, badges, and icons follow the component conventions in [references/toast-and-ui.md](references/toast-and-ui.md#component-conventions). Read them before writing any button.
114
+ - When splitting or promoting a file, update its callers in the same task.
115
+ - Installing the layer includes the required host `assets/css/main.css` and the `pathRelativeToBase` CSS entry in `nuxt.config`.
116
+
117
+ ## i18n
118
+
119
+ - Every user-facing string goes through `$t`, in templates and in script. `$t` is available in both without calling `useI18n()`.
120
+ - Add each new key to every locale file the project has (the layer ships `en.json` and `de.json` in `i18n/locales/`).
121
+ - `un.*` keys belong to this layer. `common.*` holds shared labels such as submit and cancel. App keys go under a feature namespace (`patients.single.title`).
122
+ - Examples in the references often use English literals for brevity. In real code those strings are `$t('...')` keys.
@@ -0,0 +1,135 @@
1
+ # `/nuxt-unified-ui apply`
2
+
3
+ The one pipeline that brings code in line with this skill. One subagent per file applies the skill's logical, structural, and code-style rules; the main agent picks the files, launches the subagents, and carries out the cross-file follow-ups they report.
4
+
5
+ It runs:
6
+
7
+ - **After implementation** — whenever the main agent finishes implementing work that added or edited a `.vue`, `.js`, or `.ts` file, before its final reply.
8
+ - **On request** — when the skill is invoked with the argument `apply` (`/nuxt-unified-ui apply`), or the user asks to apply nuxt-unified-ui across the project or branch.
9
+
10
+ Both run the same steps on the same file selection.
11
+
12
+ ## Main agent
13
+
14
+ Do not edit files while subagents are running.
15
+
16
+ ### 1. Pick the files
17
+
18
+ Run from the repository root.
19
+
20
+ ```bash
21
+ branch=$(git branch --show-current)
22
+ ```
23
+
24
+ If `branch` is empty (detached HEAD), stop and ask the user which files to process.
25
+
26
+ **On `dev`, `main`, or `master`** — every tracked or new, non-ignored file:
27
+
28
+ ```bash
29
+ git ls-files --cached --others --exclude-standard -- '*.vue' '*.js' '*.ts' ':!*.d.ts'
30
+ ```
31
+
32
+ **On any other branch** — files the branch added or changed relative to its base branch, including uncommitted and untracked work.
33
+
34
+ Find the base: the closest of `dev`, `main`, `master` (local first, then `origin/`), measured by commits since the merge base. Ties go to the earlier name in that order.
35
+
36
+ ```bash
37
+ for candidate in dev main master; do
38
+ for ref in "$candidate" "origin/$candidate"; do
39
+ git rev-parse --verify --quiet "$ref^{commit}" >/dev/null || continue
40
+ base=$(git merge-base HEAD "$ref") || continue
41
+ echo "$(git rev-list --count "$base"..HEAD) $ref $base"
42
+ break
43
+ done
44
+ done | sort -s -n -k1,1
45
+ ```
46
+
47
+ The first line is the base: `<commits> <ref> <merge-base sha>`. If there is no output, stop and ask the user for the base branch. Then list the files with the merge-base sha:
48
+
49
+ ```bash
50
+ {
51
+ git diff --name-only --diff-filter=AMR "$base" -- '*.vue' '*.js' '*.ts' ':!*.d.ts'
52
+ git ls-files --others --exclude-standard -- '*.vue' '*.js' '*.ts' ':!*.d.ts'
53
+ } | sort -u
54
+ ```
55
+
56
+ Deleted files are excluded. Tell the user the branch, the base (when there is one), and how many files were selected. If none were selected, stop.
57
+
58
+ ### 2. Launch one subagent per file
59
+
60
+ Launch exactly one subagent per selected file — never several files in one subagent. Run them in parallel batches. Each subagent edits only its own file, so parallel runs do not conflict.
61
+
62
+ Use this prompt:
63
+
64
+ ```text
65
+ Apply nuxt-unified-ui to this file only:
66
+ <absolute target path>
67
+
68
+ Read and follow, in full:
69
+ <absolute skill path>/references/apply.md (section "Per-file subagent")
70
+
71
+ Return the report described there.
72
+ ```
73
+
74
+ ### 3. Carry out the follow-ups
75
+
76
+ When every subagent has reported, collect their follow-ups, remove duplicates, and apply them **one at a time** in the main agent:
77
+
78
+ - **`i18n`** — add each key to every locale file the project has. Use the reported English text for English; translate for other locales when confident, otherwise use the English text and list those keys in the summary.
79
+ - **`split`** / **`move`** / **`promote`** — apply the file-structure rules from `SKILL.md`: create or move the files, then update every caller and import.
80
+ - **`other`** — apply cross-file changes that follow directly from the skill (for example a missing REST route of a resource). List anything that needs a product decision in the summary instead of guessing.
81
+
82
+ ### 4. Second round for what step 3 touched
83
+
84
+ Launch the same per-file subagent (step 2) for every `.vue`, `.js`, or `.ts` file created or edited in step 3, then apply their `i18n` follow-ups. Do not start a third round: list any other follow-ups from this round in the summary.
85
+
86
+ ### 5. Verify and summarize
87
+
88
+ Run the project's `lint` and `typecheck` scripts when `package.json` defines them, and fix failures caused by this run. Then report: files processed, files changed, follow-ups applied, follow-ups left for the user, and any subagent that failed.
89
+
90
+ ## Per-file subagent
91
+
92
+ You bring one `.vue`, `.js`, or `.ts` file in line with the nuxt-unified-ui skill.
93
+
94
+ **Boundaries**
95
+
96
+ - Edit only the target file. Never create, move, rename, or delete files, and never edit locale files — report those needs as follow-ups.
97
+ - You may read and search the rest of the codebase to understand callers and context.
98
+ - Keep every feature of the file working. Changes required by the skill's conventions (button variants, `$t` keys, SEO, API swaps) are expected; other behavior changes are not.
99
+ - Do not change the file's public API (exports, props, emits, exposed members) unless the skill requires it; report callers that would need updating as `other` follow-ups.
100
+ - Leave generated, vendored, or third-party code unchanged and report `unchanged`.
101
+
102
+ **Steps**
103
+
104
+ 1. Read `SKILL.md` (next to this `references/` folder) in full, then the whole target file.
105
+ 2. Decide what the file is (page, component, dialog, form element, server route, plugin, util, config, …) and read the references the `SKILL.md` "Before writing" table lists for it.
106
+ 3. **Logical pass** — fix in place:
107
+ - Replace hand-rolled code with the APIs in `SKILL.md`: raw `$fetch` / `useFetch` → `ufetch` / `useUFetch`; hand-built modals → dialog launchers; direct `radashi` imports → `radXxx`; and so on.
108
+ - Apply the decisions in `SKILL.md` and the rules of the references you read: page `definePageMeta.name` and SEO, dialog logic in `onClick`, named routes, `to` for navigation-only actions, a dumb `un-table`, reactive fetch gates, component conventions.
109
+ - Move user-facing literals to `$t('...')` keys. Record each new key as an `i18n` follow-up.
110
+ - Make `atoms` / `libs` imports relative.
111
+ 4. **Structural pass** — decide, do not execute:
112
+ - Two or more independent responsibilities → `split` follow-up with each responsibility and its suggested path.
113
+ - Wrong directory for its visibility (`SKILL.md` file structure) → `move` or `promote` follow-up. Search for callers in other layers before proposing it.
114
+ - Anything else that needs another file changed → `other` follow-up.
115
+ 5. **Style pass** — read [code-style.md](code-style.md) in full, then:
116
+ - Add or correct the `/* responsibility */` header. When the file needs a split, describe its current main job and keep the `split` follow-up.
117
+ - Apply every relevant rule in `code-style.md` to the whole file.
118
+ - Run every item of its "Checklist before finishing an edit" and correct what remains.
119
+ 6. Re-read the whole result and confirm the file still does everything it did.
120
+
121
+ **Report**
122
+
123
+ ```text
124
+ applied: <absolute path>
125
+ changes:
126
+ - <one line per change>
127
+ follow-ups:
128
+ - i18n: <key> = "<English text>"
129
+ - split: <responsibility> -> <suggested path>
130
+ - move: <current path> -> <suggested path> (<reason>)
131
+ - promote: <current path> -> <suggested path> (<layer that needs it>)
132
+ - other: <file> — <change needed and why>
133
+ ```
134
+
135
+ Omit empty sections. If nothing needed changing, return `unchanged: <absolute path>`.