@ai-matrx/design-system 0.21.2 → 0.21.3
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/CHANGELOG.md +6 -0
- package/dist/content-transfer.cjs +20 -6
- package/dist/content-transfer.cjs.map +1 -1
- package/dist/content-transfer.d.cts +38 -4
- package/dist/content-transfer.d.ts +38 -4
- package/dist/content-transfer.js +20 -6
- package/dist/content-transfer.js.map +1 -1
- package/dist/data-table/copy-helpers.d.cts +4 -3
- package/dist/data-table/copy-helpers.d.ts +4 -3
- package/dist/data-table/copy-types.cjs.map +1 -1
- package/dist/data-table/copy-types.d.cts +7 -2
- package/dist/data-table/copy-types.d.ts +7 -2
- package/dist/data-table/data-row-inspector.cjs +23 -6
- package/dist/data-table/data-row-inspector.cjs.map +1 -1
- package/dist/data-table/data-row-inspector.js +23 -6
- package/dist/data-table/data-row-inspector.js.map +1 -1
- package/dist/data-table/data-row-window.cjs +23 -6
- package/dist/data-table/data-row-window.cjs.map +1 -1
- package/dist/data-table/data-row-window.js +23 -6
- package/dist/data-table/data-row-window.js.map +1 -1
- package/dist/data-table/filter-engine.d.cts +4 -3
- package/dist/data-table/filter-engine.d.ts +4 -3
- package/dist/data-table/host.cjs +12 -1
- package/dist/data-table/host.cjs.map +1 -1
- package/dist/data-table/host.d.cts +16 -3
- package/dist/data-table/host.d.ts +16 -3
- package/dist/data-table/host.js +12 -1
- package/dist/data-table/host.js.map +1 -1
- package/dist/data-table/index.cjs +799 -724
- package/dist/data-table/index.cjs.map +1 -1
- package/dist/data-table/index.d.cts +15 -6
- package/dist/data-table/index.d.ts +15 -6
- package/dist/data-table/index.js +755 -680
- package/dist/data-table/index.js.map +1 -1
- package/dist/data-table/infer-filter.d.cts +4 -3
- package/dist/data-table/infer-filter.d.ts +4 -3
- package/dist/data-table/layered-filters.d.cts +4 -3
- package/dist/data-table/layered-filters.d.ts +4 -3
- package/dist/data-table/pagination.cjs +3 -0
- package/dist/data-table/pagination.cjs.map +1 -1
- package/dist/data-table/pagination.js +3 -0
- package/dist/data-table/pagination.js.map +1 -1
- package/dist/data-table/query-control.d.cts +4 -3
- package/dist/data-table/query-control.d.ts +4 -3
- package/dist/data-table/types.cjs.map +1 -1
- package/dist/data-table/types.d.cts +3 -2
- package/dist/data-table/types.d.ts +3 -2
- package/dist/data-table/url-state.d.cts +4 -3
- package/dist/data-table/url-state.d.ts +4 -3
- package/dist/data-table/uuid-cell.cjs +3 -0
- package/dist/data-table/uuid-cell.cjs.map +1 -1
- package/dist/data-table/uuid-cell.js +3 -0
- package/dist/data-table/uuid-cell.js.map +1 -1
- package/dist/data-table/xlsx.d.cts +4 -3
- package/dist/data-table/xlsx.d.ts +4 -3
- package/dist/{types-Bt-0UhEw.d.cts → types-BarjBFyG.d.cts} +3 -1
- package/dist/{types-DnnvjGV4.d.ts → types-JFlsIv-0.d.ts} +3 -1
- package/package.json +1 -1
- package/dist/extensions-DCHtNwG3.d.cts +0 -32
- package/dist/extensions-DCHtNwG3.d.ts +0 -32
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Json, Artifact, TransferOutcome, FormatAdapter, PageAction, AgentPayloadInput, TransferCapabilities, Transport, TransferPreferenceOverrides, Source, Payload, ExportProjection, Section, SurfaceHandle, TransferScope } from '@ai-matrx/kit/content-transfer';
|
|
3
3
|
import { AlchemyIcon } from './content-transfer/icon.cjs';
|
|
4
4
|
export { ALCHEMY_COPY_TRANSFORM_SVG, ALCHEMY_GLYPH_SVG } from './content-transfer/icon.cjs';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
type ContentTransferReference = {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
noun: string;
|
|
10
|
+
items: Record<string, Json>[];
|
|
11
|
+
};
|
|
12
|
+
type ContentTransferReferencePort = {
|
|
13
|
+
supports(reference: ContentTransferReference): boolean;
|
|
14
|
+
build(reference: ContentTransferReference): string | Promise<string>;
|
|
15
|
+
};
|
|
16
|
+
type ContentTransferEmailPort = {
|
|
17
|
+
send(artifact: Artifact, context: {
|
|
18
|
+
label: string;
|
|
19
|
+
signal: AbortSignal;
|
|
20
|
+
}): Promise<TransferOutcome>;
|
|
21
|
+
};
|
|
22
|
+
/** A sealed, projection-safe representation of a tabular draft's declared schema and values. */
|
|
23
|
+
declare const tableSchemaFormat: FormatAdapter;
|
|
24
|
+
/** Flat row JSON keyed by user-facing display labels, never raw table records. */
|
|
25
|
+
declare const tableDataFormat: FormatAdapter;
|
|
26
|
+
type TransferEmailAction = PageAction & {
|
|
27
|
+
prepared: true;
|
|
28
|
+
group: "email";
|
|
29
|
+
};
|
|
30
|
+
/** Prepared-only delivery actions always serialize the current draft, never a host source. */
|
|
31
|
+
declare function createTransferEmailActions(email: ContentTransferEmailPort, options?: {
|
|
32
|
+
jsonFormat?: FormatAdapter;
|
|
33
|
+
}): TransferEmailAction[];
|
|
7
34
|
|
|
8
35
|
type AlchemyOptionValues = Record<string, boolean | number>;
|
|
9
36
|
type AlchemySourceOption = {
|
|
@@ -144,6 +171,8 @@ interface MatrxCopyMenuProps {
|
|
|
144
171
|
grouped?: boolean;
|
|
145
172
|
icon?: AlchemyIcon;
|
|
146
173
|
triggerVariant?: "transparent" | "glass" | "outline";
|
|
174
|
+
controllerRef?: React.Ref<ContentTransferController>;
|
|
175
|
+
triggerHidden?: boolean;
|
|
147
176
|
}
|
|
148
177
|
/** Opinionated bridge for Matrx's existing content builders, with one transfer UI. */
|
|
149
178
|
declare function MatrxCopyMenu(props: MatrxCopyMenuProps): React.JSX.Element;
|
|
@@ -231,6 +260,8 @@ interface ContentTransferMenuProps {
|
|
|
231
260
|
/** Uses the canonical TapTargetButton presentation. */
|
|
232
261
|
triggerVariant?: "transparent" | "glass" | "outline";
|
|
233
262
|
triggerSize?: "default" | "compact";
|
|
263
|
+
/** Mount the transfer session without a visible trigger for a host-owned menu command. */
|
|
264
|
+
triggerHidden?: boolean;
|
|
234
265
|
className?: string;
|
|
235
266
|
stopPropagation?: boolean;
|
|
236
267
|
hide?: readonly ("copy" | "prepare" | "download")[];
|
|
@@ -238,6 +269,9 @@ interface ContentTransferMenuProps {
|
|
|
238
269
|
}
|
|
239
270
|
interface ContentTransferController {
|
|
240
271
|
focusTrigger(): void;
|
|
272
|
+
preparePrimary(options?: {
|
|
273
|
+
restoreFocus?: () => void;
|
|
274
|
+
}): Promise<void>;
|
|
241
275
|
prepare(source: Source | Payload, options?: {
|
|
242
276
|
id?: string;
|
|
243
277
|
label?: string;
|
|
@@ -245,4 +279,4 @@ interface ContentTransferController {
|
|
|
245
279
|
}
|
|
246
280
|
declare function ContentTransferMenu(props: ContentTransferMenuProps): React.JSX.Element;
|
|
247
281
|
|
|
248
|
-
export { type AlchemyCopyVariant, type AlchemyCustomSource, type AlchemyDetail, type AlchemyExportItem, AlchemyGlyph, type AlchemyGroomerConfig, type AlchemyGroomerPreset, type AlchemyGroomerSection, AlchemyIcon, type AlchemyOptionValues, type AlchemySourceOption, type ContentTransferCapabilities, ContentTransferCapabilitiesProvider, type ContentTransferController, ContentTransferEmailPort, ContentTransferMenu, type ContentTransferMenuProps, ContentTransferReference, type ContentTransferReferenceAction, ContentTransferReferencePort, ContentTransferSurfaceProvider, GROOMER_LEVELS, MatrxCopyMenu, type MatrxCopyMenuProps, type SerializableFormat, type TransferMenuPresentation, type TransferMenuVariant, applyGroomerPreset, buildGroomerPresetPayload, defaultGroomerSelections, groomerPresetVariants, useContentTransferCapabilities, useContentTransferSurface };
|
|
282
|
+
export { type AlchemyCopyVariant, type AlchemyCustomSource, type AlchemyDetail, type AlchemyExportItem, AlchemyGlyph, type AlchemyGroomerConfig, type AlchemyGroomerPreset, type AlchemyGroomerSection, AlchemyIcon, type AlchemyOptionValues, type AlchemySourceOption, type ContentTransferCapabilities, ContentTransferCapabilitiesProvider, type ContentTransferController, type ContentTransferEmailPort, ContentTransferMenu, type ContentTransferMenuProps, type ContentTransferReference, type ContentTransferReferenceAction, type ContentTransferReferencePort, ContentTransferSurfaceProvider, GROOMER_LEVELS, MatrxCopyMenu, type MatrxCopyMenuProps, type SerializableFormat, type TransferMenuPresentation, type TransferMenuVariant, applyGroomerPreset, buildGroomerPresetPayload, createTransferEmailActions, defaultGroomerSelections, groomerPresetVariants, tableDataFormat, tableSchemaFormat, useContentTransferCapabilities, useContentTransferSurface };
|
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Json, Artifact, TransferOutcome, FormatAdapter, PageAction, AgentPayloadInput, TransferCapabilities, Transport, TransferPreferenceOverrides, Source, Payload, ExportProjection, Section, SurfaceHandle, TransferScope } from '@ai-matrx/kit/content-transfer';
|
|
3
3
|
import { AlchemyIcon } from './content-transfer/icon.js';
|
|
4
4
|
export { ALCHEMY_COPY_TRANSFORM_SVG, ALCHEMY_GLYPH_SVG } from './content-transfer/icon.js';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
type ContentTransferReference = {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
noun: string;
|
|
10
|
+
items: Record<string, Json>[];
|
|
11
|
+
};
|
|
12
|
+
type ContentTransferReferencePort = {
|
|
13
|
+
supports(reference: ContentTransferReference): boolean;
|
|
14
|
+
build(reference: ContentTransferReference): string | Promise<string>;
|
|
15
|
+
};
|
|
16
|
+
type ContentTransferEmailPort = {
|
|
17
|
+
send(artifact: Artifact, context: {
|
|
18
|
+
label: string;
|
|
19
|
+
signal: AbortSignal;
|
|
20
|
+
}): Promise<TransferOutcome>;
|
|
21
|
+
};
|
|
22
|
+
/** A sealed, projection-safe representation of a tabular draft's declared schema and values. */
|
|
23
|
+
declare const tableSchemaFormat: FormatAdapter;
|
|
24
|
+
/** Flat row JSON keyed by user-facing display labels, never raw table records. */
|
|
25
|
+
declare const tableDataFormat: FormatAdapter;
|
|
26
|
+
type TransferEmailAction = PageAction & {
|
|
27
|
+
prepared: true;
|
|
28
|
+
group: "email";
|
|
29
|
+
};
|
|
30
|
+
/** Prepared-only delivery actions always serialize the current draft, never a host source. */
|
|
31
|
+
declare function createTransferEmailActions(email: ContentTransferEmailPort, options?: {
|
|
32
|
+
jsonFormat?: FormatAdapter;
|
|
33
|
+
}): TransferEmailAction[];
|
|
7
34
|
|
|
8
35
|
type AlchemyOptionValues = Record<string, boolean | number>;
|
|
9
36
|
type AlchemySourceOption = {
|
|
@@ -144,6 +171,8 @@ interface MatrxCopyMenuProps {
|
|
|
144
171
|
grouped?: boolean;
|
|
145
172
|
icon?: AlchemyIcon;
|
|
146
173
|
triggerVariant?: "transparent" | "glass" | "outline";
|
|
174
|
+
controllerRef?: React.Ref<ContentTransferController>;
|
|
175
|
+
triggerHidden?: boolean;
|
|
147
176
|
}
|
|
148
177
|
/** Opinionated bridge for Matrx's existing content builders, with one transfer UI. */
|
|
149
178
|
declare function MatrxCopyMenu(props: MatrxCopyMenuProps): React.JSX.Element;
|
|
@@ -231,6 +260,8 @@ interface ContentTransferMenuProps {
|
|
|
231
260
|
/** Uses the canonical TapTargetButton presentation. */
|
|
232
261
|
triggerVariant?: "transparent" | "glass" | "outline";
|
|
233
262
|
triggerSize?: "default" | "compact";
|
|
263
|
+
/** Mount the transfer session without a visible trigger for a host-owned menu command. */
|
|
264
|
+
triggerHidden?: boolean;
|
|
234
265
|
className?: string;
|
|
235
266
|
stopPropagation?: boolean;
|
|
236
267
|
hide?: readonly ("copy" | "prepare" | "download")[];
|
|
@@ -238,6 +269,9 @@ interface ContentTransferMenuProps {
|
|
|
238
269
|
}
|
|
239
270
|
interface ContentTransferController {
|
|
240
271
|
focusTrigger(): void;
|
|
272
|
+
preparePrimary(options?: {
|
|
273
|
+
restoreFocus?: () => void;
|
|
274
|
+
}): Promise<void>;
|
|
241
275
|
prepare(source: Source | Payload, options?: {
|
|
242
276
|
id?: string;
|
|
243
277
|
label?: string;
|
|
@@ -245,4 +279,4 @@ interface ContentTransferController {
|
|
|
245
279
|
}
|
|
246
280
|
declare function ContentTransferMenu(props: ContentTransferMenuProps): React.JSX.Element;
|
|
247
281
|
|
|
248
|
-
export { type AlchemyCopyVariant, type AlchemyCustomSource, type AlchemyDetail, type AlchemyExportItem, AlchemyGlyph, type AlchemyGroomerConfig, type AlchemyGroomerPreset, type AlchemyGroomerSection, AlchemyIcon, type AlchemyOptionValues, type AlchemySourceOption, type ContentTransferCapabilities, ContentTransferCapabilitiesProvider, type ContentTransferController, ContentTransferEmailPort, ContentTransferMenu, type ContentTransferMenuProps, ContentTransferReference, type ContentTransferReferenceAction, ContentTransferReferencePort, ContentTransferSurfaceProvider, GROOMER_LEVELS, MatrxCopyMenu, type MatrxCopyMenuProps, type SerializableFormat, type TransferMenuPresentation, type TransferMenuVariant, applyGroomerPreset, buildGroomerPresetPayload, defaultGroomerSelections, groomerPresetVariants, useContentTransferCapabilities, useContentTransferSurface };
|
|
282
|
+
export { type AlchemyCopyVariant, type AlchemyCustomSource, type AlchemyDetail, type AlchemyExportItem, AlchemyGlyph, type AlchemyGroomerConfig, type AlchemyGroomerPreset, type AlchemyGroomerSection, AlchemyIcon, type AlchemyOptionValues, type AlchemySourceOption, type ContentTransferCapabilities, ContentTransferCapabilitiesProvider, type ContentTransferController, type ContentTransferEmailPort, ContentTransferMenu, type ContentTransferMenuProps, type ContentTransferReference, type ContentTransferReferenceAction, type ContentTransferReferencePort, ContentTransferSurfaceProvider, GROOMER_LEVELS, MatrxCopyMenu, type MatrxCopyMenuProps, type SerializableFormat, type TransferMenuPresentation, type TransferMenuVariant, applyGroomerPreset, buildGroomerPresetPayload, createTransferEmailActions, defaultGroomerSelections, groomerPresetVariants, tableDataFormat, tableSchemaFormat, useContentTransferCapabilities, useContentTransferSurface };
|
package/dist/content-transfer.js
CHANGED
|
@@ -1497,7 +1497,12 @@ function MatrxCopyMenuSession(props) {
|
|
|
1497
1497
|
...props.referenceActions ? { referenceActions: props.referenceActions } : {},
|
|
1498
1498
|
...props.icon !== void 0 ? { icon: props.icon } : {},
|
|
1499
1499
|
...props.triggerVariant !== void 0 ? { triggerVariant: props.triggerVariant } : {},
|
|
1500
|
-
controllerRef:
|
|
1500
|
+
controllerRef: (value) => {
|
|
1501
|
+
controller.current = value;
|
|
1502
|
+
if (typeof props.controllerRef === "function") props.controllerRef(value);
|
|
1503
|
+
else if (props.controllerRef) props.controllerRef.current = value;
|
|
1504
|
+
},
|
|
1505
|
+
...props.triggerHidden ? { triggerHidden: true } : {},
|
|
1501
1506
|
label: props.label,
|
|
1502
1507
|
variants,
|
|
1503
1508
|
actions,
|
|
@@ -2053,8 +2058,9 @@ function ContentTransferMenu(props) {
|
|
|
2053
2058
|
const identityKey = props.capabilities?.identityKey ?? inherited.identityKey;
|
|
2054
2059
|
return /* @__PURE__ */ jsx11(ContentTransferMenuSession, { ...props }, identityKey ?? "anonymous");
|
|
2055
2060
|
}
|
|
2056
|
-
function ContentTransferMenuSession({ references = [], referenceActions = [], source, surface, label, projection, sections, preferences, table, formatSources, sourceFormats = true, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef, icon = "portal", triggerVariant = "transparent", triggerSize = "default" }) {
|
|
2061
|
+
function ContentTransferMenuSession({ references = [], referenceActions = [], source, surface, label, projection, sections, preferences, table, formatSources, sourceFormats = true, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef, icon = "portal", triggerVariant = "transparent", triggerSize = "default", triggerHidden = false }) {
|
|
2057
2062
|
const triggerRef = React10.useRef(null);
|
|
2063
|
+
const restoreFocusRef = React10.useRef(null);
|
|
2058
2064
|
const inherited = React10.useContext(CapabilityContext);
|
|
2059
2065
|
const nearestSurface = React10.useContext(SurfaceContext);
|
|
2060
2066
|
const fence = useFence();
|
|
@@ -2304,13 +2310,21 @@ function ContentTransferMenuSession({ references = [], referenceActions = [], so
|
|
|
2304
2310
|
);
|
|
2305
2311
|
React10.useImperativeHandle(controllerRef, () => ({
|
|
2306
2312
|
focusTrigger: () => triggerRef.current?.focus(),
|
|
2313
|
+
preparePrimary: async (options) => {
|
|
2314
|
+
if (!enabled) throw new Error("Alchemy is not enabled for this record.");
|
|
2315
|
+
if (disabled) throw new Error("Alchemy is disabled for this record.");
|
|
2316
|
+
if (hide.includes("prepare")) throw new Error("Alchemy preparation is unavailable for this record.");
|
|
2317
|
+
if (!primary) throw new Error("Alchemy has no source to prepare.");
|
|
2318
|
+
restoreFocusRef.current = options?.restoreFocus ?? null;
|
|
2319
|
+
await openWorkspace(primaryWorkspaceKey, { source: primary, label: sourceLabel });
|
|
2320
|
+
},
|
|
2307
2321
|
prepare: async (requestedSource, options) => {
|
|
2308
2322
|
const requestedLabel = options?.label ?? ("label" in requestedSource ? requestedSource.label : "content");
|
|
2309
2323
|
const requestedKey = `controller:${options?.id ?? "source"}:${materialIdentity(requestedSource)}`;
|
|
2310
2324
|
externalWorkspaceKeys.current.add(requestedKey);
|
|
2311
2325
|
await openWorkspace(requestedKey, { source: requestedSource, label: requestedLabel });
|
|
2312
2326
|
}
|
|
2313
|
-
}), [openWorkspace]);
|
|
2327
|
+
}), [disabled, enabled, hide, openWorkspace, primary, primaryWorkspaceKey, sourceLabel]);
|
|
2314
2328
|
const runAction = React10.useCallback(async (action) => {
|
|
2315
2329
|
const { controller, id } = fence.begin();
|
|
2316
2330
|
try {
|
|
@@ -2393,7 +2407,7 @@ function ContentTransferMenuSession({ references = [], referenceActions = [], so
|
|
|
2393
2407
|
] }, id);
|
|
2394
2408
|
};
|
|
2395
2409
|
return /* @__PURE__ */ jsxs6("span", { className: cn("matrx-alchemy", className), onClick: stopPropagation ? (event) => event.stopPropagation() : void 0, children: [
|
|
2396
|
-
/* @__PURE__ */ jsxs6(Popover, { open: paletteOpen, onOpenChange: (open) => {
|
|
2410
|
+
!triggerHidden ? /* @__PURE__ */ jsxs6(Popover, { open: paletteOpen, onOpenChange: (open) => {
|
|
2397
2411
|
setPaletteOpen(open);
|
|
2398
2412
|
if (open) {
|
|
2399
2413
|
setMode("copy");
|
|
@@ -2502,13 +2516,13 @@ function ContentTransferMenuSession({ references = [], referenceActions = [], so
|
|
|
2502
2516
|
] }, group) : null;
|
|
2503
2517
|
})
|
|
2504
2518
|
] })
|
|
2505
|
-
] }),
|
|
2519
|
+
] }) : null,
|
|
2506
2520
|
notice && triggerRef.current ? createPortal(/* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-feedback", children: [
|
|
2507
2521
|
/* @__PURE__ */ jsx11("span", { role: "status", children: notice }),
|
|
2508
2522
|
/* @__PURE__ */ jsx11(ReceiptLink, { target: receiptTarget }),
|
|
2509
2523
|
/* @__PURE__ */ jsx11("button", { type: "button", "aria-label": "Dismiss Alchemy feedback", onClick: () => setNotice(null), children: "\xD7" })
|
|
2510
2524
|
] }), triggerRef.current.ownerDocument.body) : null,
|
|
2511
|
-
Object.values(workspaces).map((saved) => /* @__PURE__ */ jsx11(PreparationWorkspace, { restoreFocus: () => triggerRef.current?.focus(), open: workspaceOpen && workspaceKey === saved.key, state: saved, scopes, scopeLabels: table?.scopeLabels, onClose: () => setWorkspaceOpen(false), onChange: (next) => setWorkspaces((current) => ({ ...current, [saved.key]: next })), transport, adapters, actions: preparedActions, captureScope: workspaceCaptures.current.get(saved.key) ?? capturePrimary, ...ai ? { ai } : {}, onOutcome: report }, saved.key)),
|
|
2525
|
+
Object.values(workspaces).map((saved) => /* @__PURE__ */ jsx11(PreparationWorkspace, { restoreFocus: () => restoreFocusRef.current?.() ?? triggerRef.current?.focus(), open: workspaceOpen && workspaceKey === saved.key, state: saved, scopes, scopeLabels: table?.scopeLabels, onClose: () => setWorkspaceOpen(false), onChange: (next) => setWorkspaces((current) => ({ ...current, [saved.key]: next })), transport, adapters, actions: preparedActions, captureScope: workspaceCaptures.current.get(saved.key) ?? capturePrimary, ...ai ? { ai } : {}, onOutcome: report }, saved.key)),
|
|
2512
2526
|
emailReview
|
|
2513
2527
|
] });
|
|
2514
2528
|
}
|