@vc-shell/vc-app-skill 2.2.0 → 2.3.0-pr264.08e947d

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vc-shell/vc-app-skill",
3
- "version": "2.2.0",
3
+ "version": "2.3.0-pr264.08e947d",
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.2.0
1
+ 2.3.0
@@ -1 +1 @@
1
- Synced from framework at commit 694fdf620 on 2026-07-20T17:03:47.353Z
1
+ Synced from framework at commit eb3675e7a on 2026-07-28T09:06:26.940Z
@@ -172,6 +172,8 @@ const {
172
172
 
173
173
  By default, `useAsync` shows a toast notification on failure. The notification is **deferred** via `setTimeout(0)` so that the `ErrorInterceptor` can cancel it when a blade error banner is already displayed -- this prevents duplicate toast + banner for the same error.
174
174
 
175
+ No toast is scheduled at all while the session is flagged as expired. When a platform API call returns 401, the fetch interceptor signs the user out and redirects to the login page; every data load in flight on the page being left fails at the same time. Suppressing those toasts keeps the single "session expired" message from being buried under a cascade of load errors. The `error` ref is still populated as usual -- only the toast is skipped -- and the flag is cleared when a new sign-in begins.
176
+
175
177
  ```typescript
176
178
  // Default: shows toast on error
177
179
  const { action: save } = useAsync(async () => {
@@ -419,6 +421,7 @@ const { action: save, loading: saveLoading } = useAsync(async () => saveData());
419
421
  - Errors are parsed via `parseError()` into `DisplayableError` objects that have a user-friendly `message` property.
420
422
  - Toast notifications are deferred with `setTimeout(0)` and registered via `setPendingErrorNotification`. The `ErrorInterceptor` (blade-level `onErrorCaptured`) can call `cancelPendingErrorNotification` to suppress the toast when a blade error banner is shown instead.
421
423
  - The notification module is lazy-imported to avoid circular dependencies with `@core/composables`.
424
+ - `isSessionExpired()` from `@core/utilities/sessionExpiration` gates the notification. The flag is set by the fetch interceptor on the 401 that kills the session and cleared by `useUser.signIn`. It is imported directly, not through the `@core/utilities` barrel, for the same circular-dependency reason as `pendingErrorNotifications`.
422
425
 
423
426
  <!-- internal:end -->
424
427
 
@@ -234,6 +234,24 @@ registerToolbarItem({
234
234
  });
235
235
  ```
236
236
 
237
+ ### Keyboard shortcut for a button
238
+
239
+ Add `shortcut: hotkey.mod.s` to give a button a `Cmd/Ctrl+S`-style shortcut. The button automatically shows an OS-aware `⌘S`/`Ctrl+S` tooltip and sets `aria-keyshortcuts` -- no extra wiring:
240
+
241
+ ```typescript
242
+ import { hotkey } from "@vc-shell/framework";
243
+
244
+ registerToolbarItem({
245
+ id: "save",
246
+ title: "Save",
247
+ icon: "fas fa-save",
248
+ clickHandler: () => save(),
249
+ shortcut: hotkey.mod.s,
250
+ });
251
+ ```
252
+
253
+ See [useKeyboardShortcuts](../useKeyboardShortcuts/) for the full `hotkey` builder, OS adaptation, and accessibility details.
254
+
237
255
  ## Common mistakes
238
256
 
239
257
  ### Reaching for `useToolbar` before considering the array pattern
@@ -334,18 +352,19 @@ function helperFunction() {
334
352
 
335
353
  ### IToolbarItem
336
354
 
337
- | Property | Type | Required | Description |
338
- | -------------- | ----------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------- |
339
- | `id` | `string` | Yes | Unique identifier for the button |
340
- | `title` | `string \| Ref<string> \| ComputedRef<string>` | No | Button label (supports reactive values) |
341
- | `icon` | `string \| (() => string)` | No | Icon class (e.g., `"fas fa-save"`) or a function returning one |
342
- | `clickHandler` | `(app?) => void` | No | Click callback |
343
- | `disabled` | `boolean \| ComputedRef<boolean \| undefined>` | No | Whether the button is disabled |
344
- | `isVisible` | `boolean \| Ref<boolean \| undefined> \| ComputedRef<boolean \| undefined> \| ((blade?) => boolean \| undefined)` | No | Controls button visibility |
345
- | `priority` | `number` | No | Sort order (higher = displayed first, default `0`) |
346
- | `separator` | `"left" \| "right" \| "both"` | No | Adds a visual divider next to the button |
347
- | `permissions` | `string \| string[]` | No | Required permission(s) to display the button |
348
- | `bladeId` | `string` | No | Target blade ID (auto-resolved from context) |
355
+ | Property | Type | Required | Description |
356
+ | -------------- | ----------------------------------------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
357
+ | `id` | `string` | Yes | Unique identifier for the button |
358
+ | `title` | `string \| Ref<string> \| ComputedRef<string>` | No | Button label (supports reactive values) |
359
+ | `icon` | `string \| (() => string)` | No | Icon class (e.g., `"fas fa-save"`) or a function returning one |
360
+ | `clickHandler` | `(app?) => void` | No | Click callback |
361
+ | `disabled` | `boolean \| ComputedRef<boolean \| undefined>` | No | Whether the button is disabled |
362
+ | `isVisible` | `boolean \| Ref<boolean \| undefined> \| ComputedRef<boolean \| undefined> \| ((blade?) => boolean \| undefined)` | No | Controls button visibility |
363
+ | `priority` | `number` | No | Sort order (higher = displayed first, default `0`) |
364
+ | `separator` | `"left" \| "right" \| "both"` | No | Adds a visual divider next to the button |
365
+ | `permissions` | `string \| string[]` | No | Required permission(s) to display the button |
366
+ | `bladeId` | `string` | No | Target blade ID (auto-resolved from context) |
367
+ | `shortcut` | `ShortcutDefinition` | No | Keyboard shortcut that triggers `clickHandler`; build with `hotkey.*`. See [useKeyboardShortcuts](../useKeyboardShortcuts/) |
349
368
 
350
369
  `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.
351
370
 
@@ -354,4 +373,5 @@ function helperFunction() {
354
373
  - [useBlade](../useBlade/) -- blade context that toolbar items are scoped to
355
374
  - [usePermissions](../usePermissions/) -- conditionally register toolbar items based on permissions
356
375
  - [useAsync](../useAsync/) -- wraps async operations with loading state for disabling buttons
376
+ - [useKeyboardShortcuts](../useKeyboardShortcuts/) -- the `hotkey` builder and OS-aware formatting behind the `shortcut` field
357
377
  - `IBladeToolbar` in [Core types](../../types/) — the shape used by the `:toolbar-items` array binding
@@ -21,6 +21,7 @@ The component is renderless -- it renders its default slot and passes the curren
21
21
  2. When inside a blade (`hasBlade = true`), errors are intercepted and forwarded to `bladeStack.setBladeError()`. This displays the error in the blade's built-in error banner and prevents the error from propagating to the global error handler (avoiding duplicate toast notifications).
22
22
  3. When not inside a blade or when `capture` is true, errors are captured and exposed via the slot's `error` prop.
23
23
  4. Pending `useAsync` error notifications are cancelled via `cancelPendingErrorNotification` when the blade banner takes over.
24
+ 5. If the session has expired (a platform API call returned 401 and the app is redirecting to login), the banner is skipped -- the deferred toast is still cancelled in step 4, so the failed load leaves no trace on the page being abandoned.
24
25
 
25
26
  ## Props
26
27
 
@@ -74,6 +75,7 @@ The component is renderless -- it renders its default slot and passes the curren
74
75
  - **Inside a blade**: Errors set `BladeDescriptor.error` via the stack. The blade header renders the error banner. Calling `reset` clears the blade error.
75
76
  - **Outside a blade** (with `capture`): Errors are stored in a local ref and exposed via slot props. No blade banner is involved.
76
77
  - **Error propagation**: When inside a blade, the error is stopped from propagating (prevents duplicate toasts from the global handler). The `capture` prop also stops propagation.
78
+ - **Expired session**: While the session is flagged as expired, no blade banner is set. A dead auth cookie fails every data load on the page at once, and one redirect to login is more useful than a banner on each blade being left behind.
77
79
 
78
80
  ## Exports
79
81
 
@@ -93,4 +95,5 @@ The component must be imported before use, as shown above.
93
95
 
94
96
  - `framework/core/composables/useErrorHandler/` -- the underlying composable
95
97
  - `framework/core/utilities/pendingErrorNotifications.ts` -- cancels deferred toasts
98
+ - `framework/core/utilities/sessionExpiration.ts` -- the expired-session flag that suppresses the banner
96
99
  - `framework/core/blade-navigation/` -- BladeStack error management (types the interceptor injects)