@ethisyscore/plugin-ui 1.99.0 → 1.100.1

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.
@@ -1,11 +1,15 @@
1
+ import { R as RouteMeta } from './pageHeaderTypes-PWZpiwp6.cjs';
1
2
  import { Context } from 'react';
2
3
  import { HostSidebarActionsApiShape } from '@ethisyscore/components-react';
3
4
 
4
5
  /** Host-chrome / sidebar-actions contract version (semver). Host MAJOR must equal the SDK
5
6
  * MAJOR before any publish/subscribe; otherwise the plugin hook fails closed (WI 5188 batch 2).
6
7
  * 2.1.0 added the additive `kind:"group"` action variant (collapsible Lifecycle/Share); the
7
- * major-equality gate keeps 2.0 and 2.1 mutually compatible (older hosts ignore group children). */
8
- declare const HOST_CHROME_CONTRACT_VERSION = "2.1.0";
8
+ * major-equality gate keeps 2.0 and 2.1 mutually compatible (older hosts ignore group children).
9
+ * 2.2.0 added the additive, optional `activeNavHref` on every contribution mode; an older host
10
+ * ignores the extra field (it builds its state field-by-field, never by spreading the
11
+ * contribution), so a plugin declaring it against a 2.0/2.1 host is inert, not broken. */
12
+ declare const HOST_CHROME_CONTRACT_VERSION = "2.2.0";
9
13
  type HostSidebarActionSlot = "primary" | "overflow" | "sidebar-only";
10
14
  type HostSidebarActionVariant = "default" | "danger";
11
15
  /** Fields common to every action. */
@@ -46,13 +50,13 @@ interface HostSidebarNavItem {
46
50
  * actions); `actions` = batch-2 Quick Actions for a detail entity; `detail` = full detail-nav
47
51
  * REPLACEMENT (title + nav items + back-arrow + Quick Actions). `identityEpoch` is host-stamped
48
52
  * (plan D-1), so it is not part of this plugin-published shape. */
49
- type HostSidebarContribution = {
53
+ type HostSidebarContribution = ({
50
54
  mode: "none";
51
- } | {
55
+ } & HostSidebarActiveNav) | ({
52
56
  mode: "actions";
53
57
  entityToken: string;
54
58
  actions: HostSidebarActionDescriptor[];
55
- } | {
59
+ } & HostSidebarActiveNav) | ({
56
60
  mode: "detail";
57
61
  entityToken: string;
58
62
  title: string;
@@ -61,7 +65,30 @@ type HostSidebarContribution = {
61
65
  backHref: string;
62
66
  backLabel: string;
63
67
  actions: HostSidebarActionDescriptor[];
64
- };
68
+ } & HostSidebarActiveNav);
69
+ /**
70
+ * The optional active-nav declaration carried by EVERY contribution mode (contract ≥2.2.0).
71
+ *
72
+ * `activeNavHref` is a slug-relative href — the SAME vocabulary as nav-item and navigate-action
73
+ * hrefs — naming the sidebar entry the plugin considers active for the current route. The host
74
+ * resolves it to its own nav-item id and highlights that entry; when it is absent, or names
75
+ * nothing the host is rendering, the host's URL election decides exactly as it always has.
76
+ *
77
+ * It exists because the host's election can only light an item whose href PREFIXES the current
78
+ * URL, so a sub-page whose URL does not nest under its section (`/extensions/projects/people/:id`
79
+ * belonging to "People" at `/extensions/projects/settings`) lights the module root instead. A
80
+ * plugin's information architecture is not always recoverable from its URL shape, so the plugin
81
+ * says so; {@link resolveActiveNavHref} derives it from the `routeMeta` parent chain the plugin
82
+ * already declares for breadcrumbs.
83
+ *
84
+ * It rides on `mode:"none"` too — deliberately. `"none"` is what a plugin publishes on an
85
+ * ordinary list route with no CTAs, and that is precisely the route shape whose highlight is most
86
+ * often wrong; a declaration there still contributes no actions and still does not clobber the
87
+ * static manifest.
88
+ */
89
+ interface HostSidebarActiveNav {
90
+ activeNavHref?: string;
91
+ }
65
92
  interface HostSidebarActionsCapabilities {
66
93
  readonly contractVersion: string;
67
94
  }
@@ -134,13 +161,13 @@ declare const SIDEBAR_MODE: {
134
161
  * `entityToken` is REQUIRED here — this is the published shape existing consumers narrow
135
162
  * against (`input.entityToken` is a `string` after a mode check), unchanged. To let the
136
163
  * hook derive the token instead, OMIT the property via {@link HostSidebarDerivedInput}. */
137
- type HostSidebarInput = {
164
+ type HostSidebarInput = ({
138
165
  mode: "none";
139
- } | {
166
+ } & HostSidebarActiveNav) | ({
140
167
  mode: "actions";
141
168
  entityToken: string;
142
169
  actions: HostSidebarAction[];
143
- } | {
170
+ } & HostSidebarActiveNav) | ({
144
171
  mode: "detail";
145
172
  entityToken: string;
146
173
  title: string;
@@ -149,7 +176,7 @@ type HostSidebarInput = {
149
176
  backHref: string;
150
177
  backLabel: string;
151
178
  actions: HostSidebarAction[];
152
- };
179
+ } & HostSidebarActiveNav);
153
180
  /** Token-less authoring variants: the hook derives the host-mirrored entity token from the
154
181
  * surface base + current location (`deriveHostEntityToken`) — correct by construction, since
155
182
  * the host's token guard silently drops a mismatching publish. Prefer these unless you need
@@ -158,11 +185,11 @@ type HostSidebarInput = {
158
185
  * (e.g. `entityToken: maybeToken` with an unresolved token) keeps the long-standing
159
186
  * "unresolved token → contribute nothing" semantic and is treated as `mode:"none"`, NOT
160
187
  * derived — only a truly ABSENT property derives. */
161
- type HostSidebarDerivedInput = {
188
+ type HostSidebarDerivedInput = ({
162
189
  mode: "actions";
163
190
  entityToken?: undefined;
164
191
  actions: HostSidebarAction[];
165
- } | {
192
+ } & HostSidebarActiveNav) | ({
166
193
  mode: "detail";
167
194
  entityToken?: undefined;
168
195
  title: string;
@@ -171,7 +198,7 @@ type HostSidebarDerivedInput = {
171
198
  backHref: string;
172
199
  backLabel: string;
173
200
  actions: HostSidebarAction[];
174
- };
201
+ } & HostSidebarActiveNav);
175
202
  /** What `useHostSidebarActions` accepts: the classic explicit-token shape or a derived-token variant. */
176
203
  type HostSidebarHookInput = HostSidebarInput | HostSidebarDerivedInput;
177
204
  /**
@@ -186,4 +213,58 @@ type HostSidebarHookInput = HostSidebarInput | HostSidebarDerivedInput;
186
213
  * when the host predates or is incompatible with the seam. */
187
214
  declare function useHostSidebarActions(input: HostSidebarHookInput): void;
188
215
 
189
- export { HOST_CHROME_CONTRACT_VERSION as H, SIDEBAR_MODE as S, type HostChromeDiagnostic as a, type HostSidebarAction as b, type HostSidebarActionDescriptor as c, type HostSidebarActionSlot as d, type HostSidebarActionVariant as e, type HostSidebarActionsApi as f, type HostSidebarActionsCapabilities as g, HostSidebarActionsContext as h, type HostSidebarActionsPageProps as i, type HostSidebarContribution as j, type HostSidebarInput as k, type HostSidebarNavItem as l, isHostChromeCompatible as m, type HostSidebarHookInput as n, type HostSidebarDerivedInput as o, reportHostChromeDiagnostic as r, setHostChromeDiagnosticSink as s, useHostSidebarActions as u };
216
+ /**
217
+ * Options for {@link resolveActiveNavHref}.
218
+ */
219
+ interface ResolveActiveNavHrefOptions {
220
+ /**
221
+ * The plugin's mount base (e.g. `/extensions/projects`), stripped to make the result
222
+ * slug-relative for the host confiner. Defaults to the `/extensions/<slug>` prefix of the
223
+ * pathname — the same default {@link menuConfigToHostSidebarInput} uses.
224
+ */
225
+ basePath?: string;
226
+ }
227
+ /**
228
+ * Derive the sidebar entry that owns the current route, as a slug-relative href for
229
+ * `activeNavHref` on a `useHostSidebarActions` input.
230
+ *
231
+ * The source of truth is the plugin's own `routeMeta` — the `{ pattern, label, parent }` chain it
232
+ * already declares for breadcrumbs. That chain IS the plugin's information architecture: it
233
+ * records that `/extensions/projects/people/:id` belongs under `/extensions/projects/settings`,
234
+ * which no amount of URL inspection can recover, because the two share no path. So this walks the
235
+ * matched route up its `parent` chain and returns the nearest CONCRETE (param-free) ancestor:
236
+ *
237
+ * - `/extensions/projects/people/abc` → `settings` (via parent `/extensions/projects/settings`)
238
+ * - `/extensions/tech/technologies/7/edit` → `technologies` (two parents up, past `…/:id`)
239
+ * - `/extensions/projects/all` → `all` (the route is its own nav item)
240
+ *
241
+ * Returns `undefined` — meaning "no opinion, let the host's URL election decide" — when the path
242
+ * matches no route, when no ancestor is concrete, or when the answer is the plugin ROOT. The root
243
+ * is deliberately not declarable: its href prefixes every URL in the plugin, so the URL election
244
+ * already picks it whenever nothing deeper matches, and there is no case where declaring it
245
+ * changes an outcome.
246
+ *
247
+ * The result is a HINT. The host validates it against the nav items it is actually rendering and
248
+ * falls back to its URL election if it names nothing, so a stale or mistaken `routeMeta` entry
249
+ * degrades to today's behaviour rather than blanking the sidebar.
250
+ */
251
+ declare function resolveActiveNavHref(pathname: string, routeMeta: RouteMeta, opts?: ResolveActiveNavHrefOptions): string | undefined;
252
+ /**
253
+ * Attach an active-nav declaration to an existing `useHostSidebarActions` input.
254
+ *
255
+ * The input union is discriminated on `mode`, so a plugin cannot just spread the field on without
256
+ * re-narrowing; this does it once, correctly, for every mode — including `mode:"none"`, which is
257
+ * what the input builders return on an ordinary list route and is exactly where the declaration
258
+ * matters most. Passing `undefined` returns the input untouched, so the call site needs no branch.
259
+ *
260
+ * ```ts
261
+ * const input = withActiveNavHref(
262
+ * menuConfigToHostSidebarInput(menuConfig, { pathname, onDispatch }),
263
+ * resolveActiveNavHref(pathname, routeMeta),
264
+ * );
265
+ * useHostSidebarActions(input);
266
+ * ```
267
+ */
268
+ declare function withActiveNavHref(input: HostSidebarHookInput, activeNavHref: string | undefined): HostSidebarHookInput;
269
+
270
+ export { HOST_CHROME_CONTRACT_VERSION as H, type ResolveActiveNavHrefOptions as R, SIDEBAR_MODE as S, type HostChromeDiagnostic as a, type HostSidebarAction as b, type HostSidebarActionDescriptor as c, type HostSidebarActionSlot as d, type HostSidebarActionVariant as e, type HostSidebarActionsApi as f, type HostSidebarActionsCapabilities as g, HostSidebarActionsContext as h, type HostSidebarActionsPageProps as i, type HostSidebarActiveNav as j, type HostSidebarContribution as k, type HostSidebarInput as l, type HostSidebarNavItem as m, isHostChromeCompatible as n, resolveActiveNavHref as o, type HostSidebarHookInput as p, type HostSidebarDerivedInput as q, reportHostChromeDiagnostic as r, setHostChromeDiagnosticSink as s, useHostSidebarActions as u, withActiveNavHref as w };
@@ -1,11 +1,15 @@
1
+ import { R as RouteMeta } from './pageHeaderTypes-PWZpiwp6.js';
1
2
  import { Context } from 'react';
2
3
  import { HostSidebarActionsApiShape } from '@ethisyscore/components-react';
3
4
 
4
5
  /** Host-chrome / sidebar-actions contract version (semver). Host MAJOR must equal the SDK
5
6
  * MAJOR before any publish/subscribe; otherwise the plugin hook fails closed (WI 5188 batch 2).
6
7
  * 2.1.0 added the additive `kind:"group"` action variant (collapsible Lifecycle/Share); the
7
- * major-equality gate keeps 2.0 and 2.1 mutually compatible (older hosts ignore group children). */
8
- declare const HOST_CHROME_CONTRACT_VERSION = "2.1.0";
8
+ * major-equality gate keeps 2.0 and 2.1 mutually compatible (older hosts ignore group children).
9
+ * 2.2.0 added the additive, optional `activeNavHref` on every contribution mode; an older host
10
+ * ignores the extra field (it builds its state field-by-field, never by spreading the
11
+ * contribution), so a plugin declaring it against a 2.0/2.1 host is inert, not broken. */
12
+ declare const HOST_CHROME_CONTRACT_VERSION = "2.2.0";
9
13
  type HostSidebarActionSlot = "primary" | "overflow" | "sidebar-only";
10
14
  type HostSidebarActionVariant = "default" | "danger";
11
15
  /** Fields common to every action. */
@@ -46,13 +50,13 @@ interface HostSidebarNavItem {
46
50
  * actions); `actions` = batch-2 Quick Actions for a detail entity; `detail` = full detail-nav
47
51
  * REPLACEMENT (title + nav items + back-arrow + Quick Actions). `identityEpoch` is host-stamped
48
52
  * (plan D-1), so it is not part of this plugin-published shape. */
49
- type HostSidebarContribution = {
53
+ type HostSidebarContribution = ({
50
54
  mode: "none";
51
- } | {
55
+ } & HostSidebarActiveNav) | ({
52
56
  mode: "actions";
53
57
  entityToken: string;
54
58
  actions: HostSidebarActionDescriptor[];
55
- } | {
59
+ } & HostSidebarActiveNav) | ({
56
60
  mode: "detail";
57
61
  entityToken: string;
58
62
  title: string;
@@ -61,7 +65,30 @@ type HostSidebarContribution = {
61
65
  backHref: string;
62
66
  backLabel: string;
63
67
  actions: HostSidebarActionDescriptor[];
64
- };
68
+ } & HostSidebarActiveNav);
69
+ /**
70
+ * The optional active-nav declaration carried by EVERY contribution mode (contract ≥2.2.0).
71
+ *
72
+ * `activeNavHref` is a slug-relative href — the SAME vocabulary as nav-item and navigate-action
73
+ * hrefs — naming the sidebar entry the plugin considers active for the current route. The host
74
+ * resolves it to its own nav-item id and highlights that entry; when it is absent, or names
75
+ * nothing the host is rendering, the host's URL election decides exactly as it always has.
76
+ *
77
+ * It exists because the host's election can only light an item whose href PREFIXES the current
78
+ * URL, so a sub-page whose URL does not nest under its section (`/extensions/projects/people/:id`
79
+ * belonging to "People" at `/extensions/projects/settings`) lights the module root instead. A
80
+ * plugin's information architecture is not always recoverable from its URL shape, so the plugin
81
+ * says so; {@link resolveActiveNavHref} derives it from the `routeMeta` parent chain the plugin
82
+ * already declares for breadcrumbs.
83
+ *
84
+ * It rides on `mode:"none"` too — deliberately. `"none"` is what a plugin publishes on an
85
+ * ordinary list route with no CTAs, and that is precisely the route shape whose highlight is most
86
+ * often wrong; a declaration there still contributes no actions and still does not clobber the
87
+ * static manifest.
88
+ */
89
+ interface HostSidebarActiveNav {
90
+ activeNavHref?: string;
91
+ }
65
92
  interface HostSidebarActionsCapabilities {
66
93
  readonly contractVersion: string;
67
94
  }
@@ -134,13 +161,13 @@ declare const SIDEBAR_MODE: {
134
161
  * `entityToken` is REQUIRED here — this is the published shape existing consumers narrow
135
162
  * against (`input.entityToken` is a `string` after a mode check), unchanged. To let the
136
163
  * hook derive the token instead, OMIT the property via {@link HostSidebarDerivedInput}. */
137
- type HostSidebarInput = {
164
+ type HostSidebarInput = ({
138
165
  mode: "none";
139
- } | {
166
+ } & HostSidebarActiveNav) | ({
140
167
  mode: "actions";
141
168
  entityToken: string;
142
169
  actions: HostSidebarAction[];
143
- } | {
170
+ } & HostSidebarActiveNav) | ({
144
171
  mode: "detail";
145
172
  entityToken: string;
146
173
  title: string;
@@ -149,7 +176,7 @@ type HostSidebarInput = {
149
176
  backHref: string;
150
177
  backLabel: string;
151
178
  actions: HostSidebarAction[];
152
- };
179
+ } & HostSidebarActiveNav);
153
180
  /** Token-less authoring variants: the hook derives the host-mirrored entity token from the
154
181
  * surface base + current location (`deriveHostEntityToken`) — correct by construction, since
155
182
  * the host's token guard silently drops a mismatching publish. Prefer these unless you need
@@ -158,11 +185,11 @@ type HostSidebarInput = {
158
185
  * (e.g. `entityToken: maybeToken` with an unresolved token) keeps the long-standing
159
186
  * "unresolved token → contribute nothing" semantic and is treated as `mode:"none"`, NOT
160
187
  * derived — only a truly ABSENT property derives. */
161
- type HostSidebarDerivedInput = {
188
+ type HostSidebarDerivedInput = ({
162
189
  mode: "actions";
163
190
  entityToken?: undefined;
164
191
  actions: HostSidebarAction[];
165
- } | {
192
+ } & HostSidebarActiveNav) | ({
166
193
  mode: "detail";
167
194
  entityToken?: undefined;
168
195
  title: string;
@@ -171,7 +198,7 @@ type HostSidebarDerivedInput = {
171
198
  backHref: string;
172
199
  backLabel: string;
173
200
  actions: HostSidebarAction[];
174
- };
201
+ } & HostSidebarActiveNav);
175
202
  /** What `useHostSidebarActions` accepts: the classic explicit-token shape or a derived-token variant. */
176
203
  type HostSidebarHookInput = HostSidebarInput | HostSidebarDerivedInput;
177
204
  /**
@@ -186,4 +213,58 @@ type HostSidebarHookInput = HostSidebarInput | HostSidebarDerivedInput;
186
213
  * when the host predates or is incompatible with the seam. */
187
214
  declare function useHostSidebarActions(input: HostSidebarHookInput): void;
188
215
 
189
- export { HOST_CHROME_CONTRACT_VERSION as H, SIDEBAR_MODE as S, type HostChromeDiagnostic as a, type HostSidebarAction as b, type HostSidebarActionDescriptor as c, type HostSidebarActionSlot as d, type HostSidebarActionVariant as e, type HostSidebarActionsApi as f, type HostSidebarActionsCapabilities as g, HostSidebarActionsContext as h, type HostSidebarActionsPageProps as i, type HostSidebarContribution as j, type HostSidebarInput as k, type HostSidebarNavItem as l, isHostChromeCompatible as m, type HostSidebarHookInput as n, type HostSidebarDerivedInput as o, reportHostChromeDiagnostic as r, setHostChromeDiagnosticSink as s, useHostSidebarActions as u };
216
+ /**
217
+ * Options for {@link resolveActiveNavHref}.
218
+ */
219
+ interface ResolveActiveNavHrefOptions {
220
+ /**
221
+ * The plugin's mount base (e.g. `/extensions/projects`), stripped to make the result
222
+ * slug-relative for the host confiner. Defaults to the `/extensions/<slug>` prefix of the
223
+ * pathname — the same default {@link menuConfigToHostSidebarInput} uses.
224
+ */
225
+ basePath?: string;
226
+ }
227
+ /**
228
+ * Derive the sidebar entry that owns the current route, as a slug-relative href for
229
+ * `activeNavHref` on a `useHostSidebarActions` input.
230
+ *
231
+ * The source of truth is the plugin's own `routeMeta` — the `{ pattern, label, parent }` chain it
232
+ * already declares for breadcrumbs. That chain IS the plugin's information architecture: it
233
+ * records that `/extensions/projects/people/:id` belongs under `/extensions/projects/settings`,
234
+ * which no amount of URL inspection can recover, because the two share no path. So this walks the
235
+ * matched route up its `parent` chain and returns the nearest CONCRETE (param-free) ancestor:
236
+ *
237
+ * - `/extensions/projects/people/abc` → `settings` (via parent `/extensions/projects/settings`)
238
+ * - `/extensions/tech/technologies/7/edit` → `technologies` (two parents up, past `…/:id`)
239
+ * - `/extensions/projects/all` → `all` (the route is its own nav item)
240
+ *
241
+ * Returns `undefined` — meaning "no opinion, let the host's URL election decide" — when the path
242
+ * matches no route, when no ancestor is concrete, or when the answer is the plugin ROOT. The root
243
+ * is deliberately not declarable: its href prefixes every URL in the plugin, so the URL election
244
+ * already picks it whenever nothing deeper matches, and there is no case where declaring it
245
+ * changes an outcome.
246
+ *
247
+ * The result is a HINT. The host validates it against the nav items it is actually rendering and
248
+ * falls back to its URL election if it names nothing, so a stale or mistaken `routeMeta` entry
249
+ * degrades to today's behaviour rather than blanking the sidebar.
250
+ */
251
+ declare function resolveActiveNavHref(pathname: string, routeMeta: RouteMeta, opts?: ResolveActiveNavHrefOptions): string | undefined;
252
+ /**
253
+ * Attach an active-nav declaration to an existing `useHostSidebarActions` input.
254
+ *
255
+ * The input union is discriminated on `mode`, so a plugin cannot just spread the field on without
256
+ * re-narrowing; this does it once, correctly, for every mode — including `mode:"none"`, which is
257
+ * what the input builders return on an ordinary list route and is exactly where the declaration
258
+ * matters most. Passing `undefined` returns the input untouched, so the call site needs no branch.
259
+ *
260
+ * ```ts
261
+ * const input = withActiveNavHref(
262
+ * menuConfigToHostSidebarInput(menuConfig, { pathname, onDispatch }),
263
+ * resolveActiveNavHref(pathname, routeMeta),
264
+ * );
265
+ * useHostSidebarActions(input);
266
+ * ```
267
+ */
268
+ declare function withActiveNavHref(input: HostSidebarHookInput, activeNavHref: string | undefined): HostSidebarHookInput;
269
+
270
+ export { HOST_CHROME_CONTRACT_VERSION as H, type ResolveActiveNavHrefOptions as R, SIDEBAR_MODE as S, type HostChromeDiagnostic as a, type HostSidebarAction as b, type HostSidebarActionDescriptor as c, type HostSidebarActionSlot as d, type HostSidebarActionVariant as e, type HostSidebarActionsApi as f, type HostSidebarActionsCapabilities as g, HostSidebarActionsContext as h, type HostSidebarActionsPageProps as i, type HostSidebarActiveNav as j, type HostSidebarContribution as k, type HostSidebarInput as l, type HostSidebarNavItem as m, isHostChromeCompatible as n, resolveActiveNavHref as o, type HostSidebarHookInput as p, type HostSidebarDerivedInput as q, reportHostChromeDiagnostic as r, setHostChromeDiagnosticSink as s, useHostSidebarActions as u, withActiveNavHref as w };