@vc-shell/vc-app-skill 2.8.0-pr399.6ff7b99 → 2.9.0-pr400.2a5fffb
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 +26 -0
- package/runtime/knowledge/docs/core/composables/useToolbar/useToolbar.docs.md +34 -13
- package/runtime/knowledge/docs/core/composables/useWidgets/useWidgets.docs.md +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vc-shell/vc-app-skill",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0-pr400.2a5fffb",
|
|
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.9.0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Synced from framework at commit
|
|
1
|
+
Synced from framework at commit fd1e671ba on 2026-11-02T09:58:34.572Z
|
|
@@ -99,6 +99,32 @@ defineBladeContext(
|
|
|
99
99
|
</script>
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
## Recipe: Consumers Registered from Another Module
|
|
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 hands them the context instead:
|
|
105
|
+
|
|
106
|
+
| Registration | Receives the context |
|
|
107
|
+
| ----------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
108
|
+
| `registerExternalToolbarItem` / `registerToolbarItem` | `clickHandler(ctx)`, `disabled: (ctx) => …`, `isVisible: (blade, ctx) => …` |
|
|
109
|
+
| `registerExternalWidget` / `registerWidget` | `isVisible: (blade, ctx) => …`; the component itself calls `injectBladeContext()` |
|
|
110
|
+
| `registerWidget` with `headless` | `headless.onClick(ctx)`, `headless.disabled: (ctx) => …` |
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// In the blade
|
|
114
|
+
defineBladeContext({ selectedItems });
|
|
115
|
+
|
|
116
|
+
// In another module
|
|
117
|
+
registerExternalToolbarItem({
|
|
118
|
+
id: "bulk-export",
|
|
119
|
+
title: "Export selected",
|
|
120
|
+
targetBlades: ["Products"],
|
|
121
|
+
disabled: (ctx) => !(ctx.selectedItems as Product[] | undefined)?.length,
|
|
122
|
+
clickHandler: (ctx) => exportProducts(ctx?.selectedItems as Product[]),
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
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
|
+
|
|
102
128
|
## Details
|
|
103
129
|
|
|
104
130
|
- **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.
|
|
@@ -197,6 +197,27 @@ export default defineAppModule({
|
|
|
197
197
|
|
|
198
198
|
The name match is case-insensitive. If the blade registers its own item with the same `id`, the blade's item wins. `registerToolbarItem(item, "Products")` does the same for a single blade.
|
|
199
199
|
|
|
200
|
+
### Reading the blade's data
|
|
201
|
+
|
|
202
|
+
A button registered from outside cannot call `injectBladeContext()`: its handlers run outside the blade's component tree. The framework passes them the data the blade exposed with [`defineBladeContext`](../bladeContext/) instead. `clickHandler` receives it, and so do the function forms of `disabled` and `isVisible`:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// In the target blade
|
|
206
|
+
defineBladeContext({ selectedItems });
|
|
207
|
+
|
|
208
|
+
// In the other module
|
|
209
|
+
registerExternalToolbarItem({
|
|
210
|
+
id: "bulk-export",
|
|
211
|
+
title: "Export selected",
|
|
212
|
+
icon: "lucide-download",
|
|
213
|
+
targetBlades: ["Products"],
|
|
214
|
+
disabled: (ctx) => !(ctx.selectedItems as Product[] | undefined)?.length,
|
|
215
|
+
clickHandler: (ctx) => exportProducts(ctx?.selectedItems as Product[]),
|
|
216
|
+
});
|
|
217
|
+
```
|
|
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 context. When the blade defines no context, the handlers get `{}`.
|
|
220
|
+
|
|
200
221
|
## Auto-cleanup control
|
|
201
222
|
|
|
202
223
|
By default, items registered through `useToolbar()` inside a component setup are cleared when that component unmounts (`autoCleanup: true`). Disable this for items that should persist beyond the registering component:
|
|
@@ -378,19 +399,19 @@ function helperFunction() {
|
|
|
378
399
|
|
|
379
400
|
### IToolbarItem
|
|
380
401
|
|
|
381
|
-
| Property | Type
|
|
382
|
-
| -------------- |
|
|
383
|
-
| `id` | `string`
|
|
384
|
-
| `title` | `string \| Ref<string> \| ComputedRef<string>`
|
|
385
|
-
| `icon` | `string \| (() => string)`
|
|
386
|
-
| `clickHandler` | `(
|
|
387
|
-
| `disabled` | `boolean \| ComputedRef<boolean \| undefined
|
|
388
|
-
| `isVisible` | `boolean \| Ref<boolean \| undefined> \| ComputedRef<boolean \| undefined> \| ((blade
|
|
389
|
-
| `priority` | `number`
|
|
390
|
-
| `separator` | `"left" \| "right" \| "both"`
|
|
391
|
-
| `permissions` | `string \| string[]`
|
|
392
|
-
| `bladeId` | `string`
|
|
393
|
-
| `shortcut` | `ShortcutDefinition`
|
|
402
|
+
| Property | Type | Required | Description |
|
|
403
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
404
|
+
| `id` | `string` | Yes | Unique identifier for the button |
|
|
405
|
+
| `title` | `string \| Ref<string> \| ComputedRef<string>` | No | Button label (supports reactive values) |
|
|
406
|
+
| `icon` | `string \| (() => string)` | No | Icon class (e.g., `"fas fa-save"`) or a function returning one |
|
|
407
|
+
| `clickHandler` | `(context?: BladeContext) => void` | No | Click callback; receives the blade context |
|
|
408
|
+
| `disabled` | `boolean \| ComputedRef<boolean \| undefined> \| ((context: BladeContext) => boolean \| undefined)` | No | Whether the button is disabled; a function receives the blade context |
|
|
409
|
+
| `isVisible` | `boolean \| Ref<boolean \| undefined> \| ComputedRef<boolean \| undefined> \| ((blade, context) => boolean \| undefined)` | No | Controls button visibility; a function receives the blade descriptor and the blade context |
|
|
410
|
+
| `priority` | `number` | No | Sort order (higher = displayed first, default `0`) |
|
|
411
|
+
| `separator` | `"left" \| "right" \| "both"` | No | Adds a visual divider next to the button |
|
|
412
|
+
| `permissions` | `string \| string[]` | No | Required permission(s) to display the button |
|
|
413
|
+
| `bladeId` | `string` | No | Target blade ID (auto-resolved from context) |
|
|
414
|
+
| `shortcut` | `ShortcutDefinition` | No | Keyboard shortcut that triggers `clickHandler`; build with `hotkey.*`. See [useKeyboardShortcuts](../useKeyboardShortcuts/) |
|
|
394
415
|
|
|
395
416
|
`IToolbarItem` is the shape consumed by `ToolbarService`. The blade-level array binding uses `IBladeToolbar` (see [Core types](../../types/)), a near-identical shape that the framework normalizes into `IToolbarItem` before render.
|
|
396
417
|
|
|
@@ -118,6 +118,35 @@ 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 context as its second argument:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
registerExternalWidget({
|
|
125
|
+
id: "shipping-tracker",
|
|
126
|
+
targetBlades: ["OrderDetails"],
|
|
127
|
+
component: markRaw(ShippingTracker),
|
|
128
|
+
// the blade calls defineBladeContext({ item })
|
|
129
|
+
isVisible: (blade, ctx) => (ctx.item as Order | undefined)?.status === "Shipped",
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
A headless widget registered by blade name has no component to inject from, so its `onClick` and a function `disabled` receive the context too:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
registerWidget(
|
|
137
|
+
{
|
|
138
|
+
id: "invoice",
|
|
139
|
+
title: "Invoice",
|
|
140
|
+
headless: {
|
|
141
|
+
icon: "lucide-receipt",
|
|
142
|
+
disabled: (ctx) => !ctx.item,
|
|
143
|
+
onClick: (ctx) => openInvoice((ctx?.item as Order).id),
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
"OrderDetails",
|
|
147
|
+
);
|
|
148
|
+
```
|
|
149
|
+
|
|
121
150
|
## Recipe: Listing All Widgets for a Blade
|
|
122
151
|
|
|
123
152
|
```vue
|