@vc-shell/vc-app-skill 2.9.0-pr401.d98d2aa → 2.10.0-pr401.35bf698
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/package.json +1 -1
- package/runtime/VERSION +1 -1
- package/runtime/knowledge/docs/_BUILD_HASH.md +1 -1
- package/runtime/knowledge/docs/core/composables/bladeContext/index.docs.md +19 -8
- package/runtime/knowledge/docs/core/composables/useToolbar/useToolbar.docs.md +17 -7
- package/runtime/knowledge/docs/core/composables/useWidgets/useWidgets.docs.md +5 -5
- package/runtime/knowledge/docs/ui/components/organisms/vc-blade/vc-blade.docs.md +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vc-shell/vc-app-skill",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0-pr401.35bf698",
|
|
4
4
|
"description": "AI coding skill for scaffolding and generating VirtoCommerce Shell applications. Works with Claude Code, OpenCode, Gemini, Codex, Cursor.",
|
|
5
5
|
"bin": "./bin/install.cjs",
|
|
6
6
|
"files": [
|
package/runtime/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.10.0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Synced from framework at commit
|
|
1
|
+
Synced from framework at commit 413c13a36 on 2026-11-02T10:54:29.692Z
|
|
@@ -101,13 +101,13 @@ defineBladeContext(
|
|
|
101
101
|
|
|
102
102
|
## Recipe: Consumers Registered from Another Module
|
|
103
103
|
|
|
104
|
-
Toolbar buttons and headless widgets registered by blade name from another module are plain objects, so `injectBladeContext()` has no component to run in. The framework
|
|
104
|
+
Toolbar buttons and headless widgets registered by blade name from another module are plain objects, so `injectBladeContext()` has no component to run in. The framework calls their function forms with two arguments instead: `(blade, ctx)`, the descriptor of the blade the button or widget sits on and its context. The blade comes first because `isVisible: (blade) => …` has worked that way since 1.1.37.
|
|
105
105
|
|
|
106
|
-
| Registration |
|
|
107
|
-
| ----------------------------------------------------- |
|
|
108
|
-
| `registerExternalToolbarItem` / `registerToolbarItem` | `clickHandler
|
|
109
|
-
| `registerExternalWidget` / `registerWidget` | `isVisible
|
|
110
|
-
| `registerWidget` with `headless` | `headless.onClick
|
|
106
|
+
| Registration | Called with `(blade, ctx)` |
|
|
107
|
+
| ----------------------------------------------------- | -------------------------------------------------------------- |
|
|
108
|
+
| `registerExternalToolbarItem` / `registerToolbarItem` | `clickHandler`, `disabled`, `isVisible` |
|
|
109
|
+
| `registerExternalWidget` / `registerWidget` | `isVisible`; the component itself calls `injectBladeContext()` |
|
|
110
|
+
| `registerWidget` with `headless` | `headless.onClick`, `headless.disabled` |
|
|
111
111
|
|
|
112
112
|
```typescript
|
|
113
113
|
// In the blade
|
|
@@ -118,13 +118,24 @@ registerExternalToolbarItem({
|
|
|
118
118
|
id: "bulk-export",
|
|
119
119
|
title: "Export selected",
|
|
120
120
|
targetBlades: ["Products"],
|
|
121
|
-
disabled: (ctx) => !(ctx.selectedItems as Product[] | undefined)?.length,
|
|
122
|
-
clickHandler: (ctx) => exportProducts(ctx
|
|
121
|
+
disabled: (_blade, ctx) => !(ctx.selectedItems as Product[] | undefined)?.length,
|
|
122
|
+
clickHandler: (_blade, ctx) => exportProducts(ctx.selectedItems as Product[]),
|
|
123
123
|
});
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
The values are the same unwrapped snapshot `injectBladeContext()` returns, and the state is reactive: `disabled` and `isVisible` follow the blade's refs. A blade without `defineBladeContext` gives these handlers `{}`, not an error.
|
|
127
127
|
|
|
128
|
+
`ctx` does not say which blade it came from. When one registration targets several blades, tell them apart by `blade.name`:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
registerExternalToolbarItem({
|
|
132
|
+
id: "bulk-export",
|
|
133
|
+
targetBlades: ["Products", "Offers"],
|
|
134
|
+
disabled: (_blade, ctx) => !(ctx.selectedItems as unknown[] | undefined)?.length,
|
|
135
|
+
clickHandler: (blade, ctx) => (blade?.name === "Offers" ? exportOffers(ctx.selectedItems as Offer[]) : exportProducts(ctx.selectedItems as Product[])),
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
128
139
|
## Details
|
|
129
140
|
|
|
130
141
|
- **Automatic ref unwrapping**: `defineBladeContext` shallow-unwraps all ref/computed values in the provided object. Consumers get plain values directly (`ctx.value.item` instead of `ctx.value.item.value`). This works reactively — when the source ref changes, the context updates automatically.
|
|
@@ -199,7 +199,7 @@ The name match is case-insensitive. If the blade registers its own item with the
|
|
|
199
199
|
|
|
200
200
|
### Reading the blade's data
|
|
201
201
|
|
|
202
|
-
A button registered from outside cannot call `injectBladeContext()`: its handlers run outside the blade's component tree. The framework
|
|
202
|
+
A button registered from outside cannot call `injectBladeContext()`: its handlers run outside the blade's component tree. The framework calls `clickHandler` and the function forms of `disabled` and `isVisible` with `(blade, ctx)`: the descriptor of the blade the button sits on, and the data it exposed with [`defineBladeContext`](../bladeContext/).
|
|
203
203
|
|
|
204
204
|
```typescript
|
|
205
205
|
// In the target blade
|
|
@@ -211,12 +211,22 @@ registerExternalToolbarItem({
|
|
|
211
211
|
title: "Export selected",
|
|
212
212
|
icon: "lucide-download",
|
|
213
213
|
targetBlades: ["Products"],
|
|
214
|
-
disabled: (ctx) => !(ctx.selectedItems as Product[] | undefined)?.length,
|
|
215
|
-
clickHandler: (ctx) => exportProducts(ctx
|
|
214
|
+
disabled: (_blade, ctx) => !(ctx.selectedItems as Product[] | undefined)?.length,
|
|
215
|
+
clickHandler: (_blade, ctx) => exportProducts(ctx.selectedItems as Product[]),
|
|
216
216
|
});
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
-
Refs are already unwrapped, and the state follows the blade: the button enables as soon as the user selects a row. A keyboard `shortcut` on the item gets the same
|
|
219
|
+
Refs are already unwrapped, and the state follows the blade: the button enables as soon as the user selects a row. A keyboard `shortcut` on the item gets the same arguments. When the blade defines no context, `ctx` is `{}`.
|
|
220
|
+
|
|
221
|
+
`ctx` does not say which blade it came from. With several `targetBlades`, branch on `blade.name`:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
registerExternalToolbarItem({
|
|
225
|
+
id: "bulk-export",
|
|
226
|
+
targetBlades: ["Products", "Offers"],
|
|
227
|
+
clickHandler: (blade, ctx) => (blade?.name === "Offers" ? exportOffers(ctx.selectedItems as Offer[]) : exportProducts(ctx.selectedItems as Product[])),
|
|
228
|
+
});
|
|
229
|
+
```
|
|
220
230
|
|
|
221
231
|
## Auto-cleanup control
|
|
222
232
|
|
|
@@ -404,9 +414,9 @@ function helperFunction() {
|
|
|
404
414
|
| `id` | `string` | Yes | Unique identifier for the button |
|
|
405
415
|
| `title` | `string \| Ref<string> \| ComputedRef<string>` | No | Button label (supports reactive values) |
|
|
406
416
|
| `icon` | `string \| (() => string)` | No | Icon class (e.g., `"fas fa-save"`) or a function returning one |
|
|
407
|
-
| `clickHandler` | `(context
|
|
408
|
-
| `disabled` | `boolean \| ComputedRef<boolean \| undefined> \| ((context
|
|
409
|
-
| `isVisible` | `boolean \| Ref<boolean \| undefined> \| ComputedRef<boolean \| undefined> \| ((blade, context) => boolean \| undefined)` | No | Controls button visibility; a function receives the blade
|
|
417
|
+
| `clickHandler` | `(blade, context) => void` | No | Click callback; receives the blade and its context |
|
|
418
|
+
| `disabled` | `boolean \| ComputedRef<boolean \| undefined> \| ((blade, context) => boolean \| undefined)` | No | Whether the button is disabled; a function receives the blade and its context |
|
|
419
|
+
| `isVisible` | `boolean \| Ref<boolean \| undefined> \| ComputedRef<boolean \| undefined> \| ((blade, context) => boolean \| undefined)` | No | Controls button visibility; a function receives the blade and its context |
|
|
410
420
|
| `priority` | `number` | No | Sort order (higher = displayed first, default `0`) |
|
|
411
421
|
| `separator` | `"left" \| "right" \| "both"` | No | Adds a visual divider next to the button |
|
|
412
422
|
| `permissions` | `string \| string[]` | No | Required permission(s) to display the button |
|
|
@@ -118,7 +118,7 @@ export default {
|
|
|
118
118
|
};
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
The widget component reads the blade's data with [`injectBladeContext()`](../bladeContext/), since it renders inside the blade. `isVisible` runs before the component exists, so a function form receives the blade
|
|
121
|
+
The widget component reads the blade's data with [`injectBladeContext()`](../bladeContext/), since it renders inside the blade. `isVisible` runs before the component exists, so a function form receives `(blade, ctx)`: the blade descriptor and the blade context.
|
|
122
122
|
|
|
123
123
|
```typescript
|
|
124
124
|
registerExternalWidget({
|
|
@@ -126,11 +126,11 @@ registerExternalWidget({
|
|
|
126
126
|
targetBlades: ["OrderDetails"],
|
|
127
127
|
component: markRaw(ShippingTracker),
|
|
128
128
|
// the blade calls defineBladeContext({ item })
|
|
129
|
-
isVisible: (
|
|
129
|
+
isVisible: (_blade, ctx) => (ctx.item as Order | undefined)?.status === "Shipped",
|
|
130
130
|
});
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
A headless widget registered by blade name has no component to inject from, so its `onClick` and a function `disabled` receive
|
|
133
|
+
A headless widget registered by blade name has no component to inject from, so its `onClick` and a function `disabled` receive `(blade, ctx)` too:
|
|
134
134
|
|
|
135
135
|
```typescript
|
|
136
136
|
registerWidget(
|
|
@@ -139,8 +139,8 @@ registerWidget(
|
|
|
139
139
|
title: "Invoice",
|
|
140
140
|
headless: {
|
|
141
141
|
icon: "lucide-receipt",
|
|
142
|
-
disabled: (ctx) => !ctx.item,
|
|
143
|
-
onClick: (ctx) => openInvoice((ctx
|
|
142
|
+
disabled: (_blade, ctx) => !ctx.item,
|
|
143
|
+
onClick: (_blade, ctx) => openInvoice((ctx.item as Order).id),
|
|
144
144
|
},
|
|
145
145
|
},
|
|
146
146
|
"OrderDetails",
|
|
@@ -359,9 +359,10 @@ interface IBladeToolbar {
|
|
|
359
359
|
id?: string;
|
|
360
360
|
title?: string | Ref<string> | ComputedRef<string>;
|
|
361
361
|
icon?: string | (() => string);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
// function forms get (blade, context): the blade descriptor and the blade context
|
|
363
|
+
clickHandler?(blade: BladeDescriptor | undefined, context: BladeContext): void;
|
|
364
|
+
disabled?: boolean | ComputedRef<boolean | undefined> | ((blade: BladeDescriptor | undefined, context: BladeContext) => boolean | undefined);
|
|
365
|
+
isVisible?: boolean | Ref<boolean | undefined> | ComputedRef<boolean | undefined> | ((blade: BladeDescriptor | undefined, context: BladeContext) => boolean | undefined);
|
|
365
366
|
separator?: "left" | "right" | "both";
|
|
366
367
|
permissions?: string | string[];
|
|
367
368
|
}
|