@mohamedatia/fly-design-system 2.10.0 → 2.12.0
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/fesm2022/mohamedatia-fly-design-system.mjs +686 -7
- package/fesm2022/mohamedatia-fly-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/scss/_theme-light.scss +7 -1
- package/types/mohamedatia-fly-design-system.d.ts +644 -13
- package/types/mohamedatia-fly-design-system.d.ts.map +1 -1
|
@@ -41,6 +41,29 @@ const FLYOS_LAUNCH_EVENT = 'flyos:launch';
|
|
|
41
41
|
* Subsequent re-launches into the already-mounted remote arrive via the event.
|
|
42
42
|
*/
|
|
43
43
|
const FlyosPendingLaunchesGlobalKey = '__FLYOS_PENDING_LAUNCHES__';
|
|
44
|
+
/**
|
|
45
|
+
* Per-window injection token carrying the active <see cref="WindowHelpHint"/>
|
|
46
|
+
* as a writable signal. The shell creates one writable signal per window
|
|
47
|
+
* (alongside <see cref="LAUNCH_CONTEXT"/>); apps inject it and `.set(...)` to
|
|
48
|
+
* publish or update their hint. Setting `null` clears the hint — the chrome
|
|
49
|
+
* falls back to the implicit `win.appId` deeplink.
|
|
50
|
+
*
|
|
51
|
+
* **Federation note:** same caveat as <see cref="LAUNCH_CONTEXT"/> — Native
|
|
52
|
+
* Federation can split the InjectionToken across host/remote bundles, so
|
|
53
|
+
* federated remotes cannot rely on DI to publish hints. Use
|
|
54
|
+
* <see cref="FLY_WINDOW_HELP_HINT_EVENT"/> instead.
|
|
55
|
+
*/
|
|
56
|
+
const WINDOW_HELP_HINT = new InjectionToken('WINDOW_HELP_HINT');
|
|
57
|
+
/**
|
|
58
|
+
* Federation-safe window CustomEvent name for publishing a help hint from a
|
|
59
|
+
* federated remote that cannot see <see cref="WINDOW_HELP_HINT"/> via DI.
|
|
60
|
+
*
|
|
61
|
+
* Detail: <see cref="FlyWindowHelpHintEventDetail"/>. The shell listens at
|
|
62
|
+
* `window` scope and mirrors the payload into the matching per-window signal
|
|
63
|
+
* (keyed by `windowId` from <see cref="WINDOW_DATA"/>). Pairs with the
|
|
64
|
+
* <see cref="FLYOS_LAUNCH_EVENT"/> pattern.
|
|
65
|
+
*/
|
|
66
|
+
const FLY_WINDOW_HELP_HINT_EVENT = 'flyos:window-help-hint';
|
|
44
67
|
|
|
45
68
|
/**
|
|
46
69
|
* Generic share / ACL panel models — hosts map domain DTOs to these shapes.
|
|
@@ -799,6 +822,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
799
822
|
args: [{ providedIn: 'root' }]
|
|
800
823
|
}] });
|
|
801
824
|
|
|
825
|
+
/**
|
|
826
|
+
* Publishes a {@link WindowHelpHint} for a window so the shell's titlebar Help
|
|
827
|
+
* button deeplinks the help-center reader to the article most relevant to where
|
|
828
|
+
* the user is. The **publisher twin** of the shell's `WindowHelpHintService`
|
|
829
|
+
* (the listener): it exists in the design system so that **any app — in-shell
|
|
830
|
+
* OS-Core app or federated remote — publishes hints the same way**, without
|
|
831
|
+
* hand-rolling the cross-bundle CustomEvent contract.
|
|
832
|
+
*
|
|
833
|
+
* **Why a `window` CustomEvent and not the `WINDOW_HELP_HINT` DI token?**
|
|
834
|
+
* Native Federation can split an `InjectionToken` instance across host and
|
|
835
|
+
* remote bundles, so a remote that injects `WINDOW_HELP_HINT` may receive a
|
|
836
|
+
* different token than the one the shell provides. The string-keyed
|
|
837
|
+
* {@link FLY_WINDOW_HELP_HINT_EVENT} crosses bundles reliably; the shell mirrors
|
|
838
|
+
* it into the matching per-window hint signal.
|
|
839
|
+
*
|
|
840
|
+
* **Why a per-window handle and not `bindWindow`/`setHint` state?**
|
|
841
|
+
* This service is `providedIn: 'root'` and the design system is a shared
|
|
842
|
+
* Native-Federation singleton, so a *single* instance is shared across the
|
|
843
|
+
* shell and every concurrently-open window (multiple in-shell apps, multiple
|
|
844
|
+
* remotes). A handle closes over its window id, so two windows never clobber a
|
|
845
|
+
* shared "current window" — each handle targets only its own titlebar.
|
|
846
|
+
*/
|
|
847
|
+
class FlyWindowHelpService {
|
|
848
|
+
/**
|
|
849
|
+
* Returns a publisher bound to a single window. Call once per window (e.g.
|
|
850
|
+
* from the app root with `WINDOW_DATA.id`) and keep the handle. A
|
|
851
|
+
* null/undefined id (standalone, no shell) yields a handle whose `setHint`
|
|
852
|
+
* is a no-op.
|
|
853
|
+
*/
|
|
854
|
+
forWindow(windowId) {
|
|
855
|
+
const id = windowId ?? null;
|
|
856
|
+
return {
|
|
857
|
+
setHint: (hint) => {
|
|
858
|
+
if (typeof window === 'undefined' || !id)
|
|
859
|
+
return;
|
|
860
|
+
const detail = { windowId: id, hint };
|
|
861
|
+
window.dispatchEvent(new CustomEvent(FLY_WINDOW_HELP_HINT_EVENT, { detail }));
|
|
862
|
+
},
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyWindowHelpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
866
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyWindowHelpService, providedIn: 'root' });
|
|
867
|
+
}
|
|
868
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: FlyWindowHelpService, decorators: [{
|
|
869
|
+
type: Injectable,
|
|
870
|
+
args: [{ providedIn: 'root' }]
|
|
871
|
+
}] });
|
|
872
|
+
|
|
802
873
|
/**
|
|
803
874
|
* fly-remote-styles — Shell-layer CSS loader for Native Federation remotes.
|
|
804
875
|
*
|
|
@@ -3359,7 +3430,7 @@ class AgentCommandRegistry {
|
|
|
3359
3430
|
* for reactive filtering.
|
|
3360
3431
|
*/
|
|
3361
3432
|
visible(liveAppIds) {
|
|
3362
|
-
const liveSignal = isSignal(liveAppIds) ? liveAppIds : signal(liveAppIds).asReadonly();
|
|
3433
|
+
const liveSignal = isSignal$1(liveAppIds) ? liveAppIds : signal(liveAppIds).asReadonly();
|
|
3363
3434
|
return computed(() => {
|
|
3364
3435
|
const live = liveSignal();
|
|
3365
3436
|
return this._commands().filter((cmd) => {
|
|
@@ -3432,6 +3503,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
3432
3503
|
args: [{ providedIn: 'root' }]
|
|
3433
3504
|
}] });
|
|
3434
3505
|
/** `isSignal` shim — narrows to either `Signal<T>` or a plain value. */
|
|
3506
|
+
function isSignal$1(v) {
|
|
3507
|
+
return typeof v === 'function';
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
/**
|
|
3511
|
+
* Singleton registry of entity lookups offered by the `/lookup` typeahead.
|
|
3512
|
+
*
|
|
3513
|
+
* Mirrors {@link AgentCommandRegistry} exactly — same federation-singleton story
|
|
3514
|
+
* (`sharedMappings: ['@mohamedatia/fly-design-system']`), same id-collision
|
|
3515
|
+
* "latest wins" contract, same disposable-handle ergonomics. Federated remotes
|
|
3516
|
+
* register their lookupable entities at boot (Circles: scenario / trend / signal)
|
|
3517
|
+
* and dispose on window close, so the picker only ever offers entities whose app
|
|
3518
|
+
* is currently live.
|
|
3519
|
+
*
|
|
3520
|
+
* Storage is a signal store keyed on {@link LookupRegistration.entity}. Because
|
|
3521
|
+
* `entity` is the collision key, an app re-registering the same entity replaces
|
|
3522
|
+
* the prior descriptor; a stale handle's `dispose()` then no-ops.
|
|
3523
|
+
*/
|
|
3524
|
+
class AgentLookupRegistry {
|
|
3525
|
+
_lookups = signal([], ...(ngDevMode ? [{ debugName: "_lookups" }] : /* istanbul ignore next */ []));
|
|
3526
|
+
/** All currently-registered lookups, in insertion order. */
|
|
3527
|
+
all = this._lookups.asReadonly();
|
|
3528
|
+
/**
|
|
3529
|
+
* Lookups whose scope is `'global'` OR whose `scope.appId` is in the live app
|
|
3530
|
+
* set. Recomputes when either the registry or `liveAppIds` changes — pass a
|
|
3531
|
+
* `Signal<ReadonlySet<string>>` from the host's app-registry for reactive
|
|
3532
|
+
* filtering, exactly like {@link AgentCommandRegistry.visible}.
|
|
3533
|
+
*/
|
|
3534
|
+
visible(liveAppIds) {
|
|
3535
|
+
const liveSignal = isSignal(liveAppIds)
|
|
3536
|
+
? liveAppIds
|
|
3537
|
+
: signal(liveAppIds).asReadonly();
|
|
3538
|
+
return computed(() => {
|
|
3539
|
+
const live = liveSignal();
|
|
3540
|
+
return this._lookups().filter((l) => {
|
|
3541
|
+
if (l.scope === 'global')
|
|
3542
|
+
return true;
|
|
3543
|
+
return live.has(l.scope.appId);
|
|
3544
|
+
});
|
|
3545
|
+
});
|
|
3546
|
+
}
|
|
3547
|
+
/**
|
|
3548
|
+
* Register one lookup. Returns a handle whose `dispose()` removes the row by
|
|
3549
|
+
* `entity`. A later re-registration of the same entity makes the original
|
|
3550
|
+
* handle's `dispose()` a no-op (the newer registration owns the row).
|
|
3551
|
+
*/
|
|
3552
|
+
register(lookup) {
|
|
3553
|
+
const generation = ++this._generation;
|
|
3554
|
+
this._lookups.update((rows) => [
|
|
3555
|
+
...rows.filter((r) => r.entity !== lookup.entity),
|
|
3556
|
+
lookup,
|
|
3557
|
+
]);
|
|
3558
|
+
this._owners.set(lookup.entity, generation);
|
|
3559
|
+
return {
|
|
3560
|
+
dispose: () => {
|
|
3561
|
+
if (this._owners.get(lookup.entity) === generation) {
|
|
3562
|
+
this._owners.delete(lookup.entity);
|
|
3563
|
+
this._lookups.update((rows) => rows.filter((r) => r.entity !== lookup.entity));
|
|
3564
|
+
}
|
|
3565
|
+
},
|
|
3566
|
+
};
|
|
3567
|
+
}
|
|
3568
|
+
/**
|
|
3569
|
+
* Bulk register. Rolls back on a duplicate entity WITHIN the input batch
|
|
3570
|
+
* (throws before any row lands). Cross-batch duplicates against existing rows
|
|
3571
|
+
* follow the standard "latest wins" rule and do NOT trigger rollback.
|
|
3572
|
+
*/
|
|
3573
|
+
registerAll(lookups) {
|
|
3574
|
+
const seen = new Set();
|
|
3575
|
+
for (const l of lookups) {
|
|
3576
|
+
if (seen.has(l.entity)) {
|
|
3577
|
+
throw new Error(`AgentLookupRegistry.registerAll: duplicate entity "${l.entity}" in batch`);
|
|
3578
|
+
}
|
|
3579
|
+
seen.add(l.entity);
|
|
3580
|
+
}
|
|
3581
|
+
const handles = lookups.map((l) => this.register(l));
|
|
3582
|
+
let disposed = false;
|
|
3583
|
+
return {
|
|
3584
|
+
dispose: () => {
|
|
3585
|
+
if (disposed)
|
|
3586
|
+
return;
|
|
3587
|
+
disposed = true;
|
|
3588
|
+
for (const h of handles)
|
|
3589
|
+
h.dispose();
|
|
3590
|
+
},
|
|
3591
|
+
};
|
|
3592
|
+
}
|
|
3593
|
+
/** Tear down by entity. Idempotent. */
|
|
3594
|
+
unregister(entity) {
|
|
3595
|
+
if (this._owners.delete(entity)) {
|
|
3596
|
+
this._lookups.update((rows) => rows.filter((r) => r.entity !== entity));
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
/** Monotonic counter; identifies which registration call currently owns each entity. */
|
|
3600
|
+
_generation = 0;
|
|
3601
|
+
/** entity → generation. Lets a stale handle's `dispose()` no-op after replacement. */
|
|
3602
|
+
_owners = new Map();
|
|
3603
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentLookupRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3604
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentLookupRegistry, providedIn: 'root' });
|
|
3605
|
+
}
|
|
3606
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentLookupRegistry, decorators: [{
|
|
3607
|
+
type: Injectable,
|
|
3608
|
+
args: [{ providedIn: 'root' }]
|
|
3609
|
+
}] });
|
|
3610
|
+
/** `isSignal` shim — narrows to either `Signal<T>` or a plain value. */
|
|
3435
3611
|
function isSignal(v) {
|
|
3436
3612
|
return typeof v === 'function';
|
|
3437
3613
|
}
|
|
@@ -3539,6 +3715,21 @@ function compositeKey(kind, appId) {
|
|
|
3539
3715
|
return `${kind}::${appId}`;
|
|
3540
3716
|
}
|
|
3541
3717
|
|
|
3718
|
+
/**
|
|
3719
|
+
* Thrown synchronously by {@link AgentActionBus.dispatch} when a caller
|
|
3720
|
+
* supplies a dispatch mode this DS version doesn't implement yet. Catching
|
|
3721
|
+
* by class name lets a forward-compatible caller fall back to `'stage'`
|
|
3722
|
+
* without depending on instanceof across federation boundaries.
|
|
3723
|
+
*/
|
|
3724
|
+
class AgentActionUnsupportedDispatchError extends Error {
|
|
3725
|
+
dispatch;
|
|
3726
|
+
constructor(dispatch) {
|
|
3727
|
+
super(`AgentActionBus: dispatch="${dispatch}" not supported in this DS version`);
|
|
3728
|
+
this.dispatch = dispatch;
|
|
3729
|
+
this.name = 'AgentActionUnsupportedDispatchError';
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3542
3733
|
/**
|
|
3543
3734
|
* Agent input contracts.
|
|
3544
3735
|
*
|
|
@@ -3558,12 +3749,40 @@ function compositeKey(kind, appId) {
|
|
|
3558
3749
|
*/
|
|
3559
3750
|
/** Frozen MIME used by `flyAgentDraggable` and the drop-zone reader. Never change without a DS major. */
|
|
3560
3751
|
const AGENT_DRAG_MIME = 'application/x-fly-agent-payload+json';
|
|
3561
|
-
/**
|
|
3562
|
-
|
|
3752
|
+
/**
|
|
3753
|
+
* Frozen payload envelope version.
|
|
3754
|
+
*
|
|
3755
|
+
* v1 — minimal drag payload: kind / appId / version / payload / plainTextFallback /
|
|
3756
|
+
* suggestedCommandIds. Still used by the drag/drop surface.
|
|
3757
|
+
*
|
|
3758
|
+
* v2 — bus envelope ({@link AgentMessageEnvelope}). Adds optional `userMessage`,
|
|
3759
|
+
* `systemContext`, `attachments`, `mcpScope` so a dispatcher can give the agent
|
|
3760
|
+
* rich context without polluting the user-visible message bubble. v2 is a
|
|
3761
|
+
* superset of v1 — every v1 payload is a valid v2 payload — so the version
|
|
3762
|
+
* number ratchets forward without breaking existing callers.
|
|
3763
|
+
*/
|
|
3764
|
+
const AGENT_PAYLOAD_VERSION = 2;
|
|
3765
|
+
/**
|
|
3766
|
+
* Versions accepted by the validator. v1 payloads (the drag/drop surface) remain valid;
|
|
3767
|
+
* v2 adds the optional bus-envelope fields. Renderers narrow on `version` when they need
|
|
3768
|
+
* to.
|
|
3769
|
+
*/
|
|
3770
|
+
const SUPPORTED_AGENT_PAYLOAD_VERSIONS = Object.freeze([1, 2]);
|
|
3563
3771
|
const DEFAULT_AGENT_PAYLOAD_LIMITS = Object.freeze({
|
|
3564
3772
|
maxJsonBytes: 32 * 1024,
|
|
3565
3773
|
maxPlainTextFallbackBytes: 8 * 1024,
|
|
3566
3774
|
maxStringFieldBytes: 4 * 1024,
|
|
3775
|
+
maxUserMessageBytes: 280 * 4, // 280 chars × 4 bytes/char worst-case UTF-8
|
|
3776
|
+
maxSystemContextEntries: 32,
|
|
3777
|
+
maxSystemContextValueBytes: 4 * 1024,
|
|
3778
|
+
maxSystemContextJsonBytes: 8 * 1024,
|
|
3779
|
+
maxAttachments: 4,
|
|
3780
|
+
maxAttachmentJsonBytes: 16 * 1024,
|
|
3781
|
+
maxAttachmentTextBytes: 8 * 1024,
|
|
3782
|
+
maxAttachmentDataUrlBytes: 32 * 1024,
|
|
3783
|
+
maxMcpScopeApis: 5,
|
|
3784
|
+
maxMcpScopeApiBytes: 128,
|
|
3785
|
+
maxDeepLinkRouteBytes: 1024,
|
|
3567
3786
|
});
|
|
3568
3787
|
|
|
3569
3788
|
/**
|
|
@@ -3669,18 +3888,21 @@ function trimAgentString(input, maxBytes, ellipsis = '…') {
|
|
|
3669
3888
|
* structured failure with the offending field path and byte counts.
|
|
3670
3889
|
*
|
|
3671
3890
|
* Order of checks (cheap → expensive):
|
|
3672
|
-
* 1. `version`
|
|
3891
|
+
* 1. `version` is in {@link SUPPORTED_AGENT_PAYLOAD_VERSIONS}.
|
|
3673
3892
|
* 2. `kind` is a non-empty string.
|
|
3674
3893
|
* 3. `plainTextFallback` fits its cap.
|
|
3675
3894
|
* 4. Every string in `payload` (recursively) fits the per-field cap.
|
|
3676
|
-
* 5.
|
|
3895
|
+
* 5. v2 sections — `userMessage`, `systemContext`, `attachments`, `mcpScope` —
|
|
3896
|
+
* each fits their independent caps. Skipped when absent so v1 payloads pass
|
|
3897
|
+
* unchanged.
|
|
3898
|
+
* 6. The full `JSON.stringify(envelope)` fits the JSON cap.
|
|
3677
3899
|
*
|
|
3678
3900
|
* The walker stops on the first oversize string field — the intent is to surface
|
|
3679
3901
|
* actionable feedback, not enumerate every offender.
|
|
3680
3902
|
*/
|
|
3681
3903
|
function validateAgentPayload(payload, limits) {
|
|
3682
3904
|
const eff = { ...DEFAULT_AGENT_PAYLOAD_LIMITS, ...(limits ?? {}) };
|
|
3683
|
-
if (payload.version
|
|
3905
|
+
if (!SUPPORTED_AGENT_PAYLOAD_VERSIONS.includes(payload.version)) {
|
|
3684
3906
|
return { ok: false, reason: 'invalid_version' };
|
|
3685
3907
|
}
|
|
3686
3908
|
if (typeof payload.kind !== 'string' || payload.kind.length === 0) {
|
|
@@ -3699,6 +3921,16 @@ function validateAgentPayload(payload, limits) {
|
|
|
3699
3921
|
const fieldFailure = walkStringsForOverage(payload.payload, 'payload', eff.maxStringFieldBytes);
|
|
3700
3922
|
if (fieldFailure)
|
|
3701
3923
|
return fieldFailure;
|
|
3924
|
+
// v2 — per-section caps. Each helper is a no-op when its section is absent
|
|
3925
|
+
// so a v1 payload (no userMessage / systemContext / attachments / mcpScope /
|
|
3926
|
+
// deepLinkRoute) sails through unchanged.
|
|
3927
|
+
const v2Failure = checkUserMessage(payload, eff) ??
|
|
3928
|
+
checkSystemContext(payload, eff) ??
|
|
3929
|
+
checkAttachments(payload, eff) ??
|
|
3930
|
+
checkMcpScope(payload, eff) ??
|
|
3931
|
+
checkDeepLinkRoute(payload, eff);
|
|
3932
|
+
if (v2Failure)
|
|
3933
|
+
return v2Failure;
|
|
3702
3934
|
const json = safeStringify(payload);
|
|
3703
3935
|
const jsonBytes = utf8ByteLength(json);
|
|
3704
3936
|
if (jsonBytes > eff.maxJsonBytes) {
|
|
@@ -3711,6 +3943,200 @@ function validateAgentPayload(payload, limits) {
|
|
|
3711
3943
|
}
|
|
3712
3944
|
return { ok: true };
|
|
3713
3945
|
}
|
|
3946
|
+
// ─── v2 section validators ──────────────────────────────────────────────────
|
|
3947
|
+
function checkUserMessage(payload, eff) {
|
|
3948
|
+
if (payload.userMessage === undefined)
|
|
3949
|
+
return null;
|
|
3950
|
+
if (typeof payload.userMessage !== 'string')
|
|
3951
|
+
return null; // structural; ts catches at the call site
|
|
3952
|
+
const bytes = utf8ByteLength(payload.userMessage);
|
|
3953
|
+
if (bytes > eff.maxUserMessageBytes) {
|
|
3954
|
+
return {
|
|
3955
|
+
ok: false,
|
|
3956
|
+
reason: 'user_message_too_large',
|
|
3957
|
+
fieldPath: 'userMessage',
|
|
3958
|
+
actualBytes: bytes,
|
|
3959
|
+
limitBytes: eff.maxUserMessageBytes,
|
|
3960
|
+
};
|
|
3961
|
+
}
|
|
3962
|
+
return null;
|
|
3963
|
+
}
|
|
3964
|
+
function checkSystemContext(payload, eff) {
|
|
3965
|
+
const ctx = payload.systemContext;
|
|
3966
|
+
if (!ctx)
|
|
3967
|
+
return null;
|
|
3968
|
+
const keys = Object.keys(ctx);
|
|
3969
|
+
if (keys.length > eff.maxSystemContextEntries) {
|
|
3970
|
+
return {
|
|
3971
|
+
ok: false,
|
|
3972
|
+
reason: 'system_context_too_many_entries',
|
|
3973
|
+
fieldPath: 'systemContext',
|
|
3974
|
+
actualBytes: keys.length,
|
|
3975
|
+
limitBytes: eff.maxSystemContextEntries,
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
for (const k of keys) {
|
|
3979
|
+
const v = ctx[k];
|
|
3980
|
+
if (typeof v !== 'string')
|
|
3981
|
+
continue;
|
|
3982
|
+
const bytes = utf8ByteLength(v);
|
|
3983
|
+
if (bytes > eff.maxSystemContextValueBytes) {
|
|
3984
|
+
return {
|
|
3985
|
+
ok: false,
|
|
3986
|
+
reason: 'system_context_value_too_large',
|
|
3987
|
+
fieldPath: `systemContext.${k}`,
|
|
3988
|
+
actualBytes: bytes,
|
|
3989
|
+
limitBytes: eff.maxSystemContextValueBytes,
|
|
3990
|
+
};
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
const ctxJsonBytes = utf8ByteLength(safeStringify(ctx));
|
|
3994
|
+
if (ctxJsonBytes > eff.maxSystemContextJsonBytes) {
|
|
3995
|
+
return {
|
|
3996
|
+
ok: false,
|
|
3997
|
+
reason: 'system_context_json_too_large',
|
|
3998
|
+
fieldPath: 'systemContext',
|
|
3999
|
+
actualBytes: ctxJsonBytes,
|
|
4000
|
+
limitBytes: eff.maxSystemContextJsonBytes,
|
|
4001
|
+
};
|
|
4002
|
+
}
|
|
4003
|
+
return null;
|
|
4004
|
+
}
|
|
4005
|
+
function checkAttachments(payload, eff) {
|
|
4006
|
+
const atts = payload.attachments;
|
|
4007
|
+
if (!atts)
|
|
4008
|
+
return null;
|
|
4009
|
+
if (atts.length > eff.maxAttachments) {
|
|
4010
|
+
return {
|
|
4011
|
+
ok: false,
|
|
4012
|
+
reason: 'too_many_attachments',
|
|
4013
|
+
fieldPath: 'attachments',
|
|
4014
|
+
actualBytes: atts.length,
|
|
4015
|
+
limitBytes: eff.maxAttachments,
|
|
4016
|
+
};
|
|
4017
|
+
}
|
|
4018
|
+
for (let i = 0; i < atts.length; i++) {
|
|
4019
|
+
const a = atts[i];
|
|
4020
|
+
const path = `attachments[${i}]`;
|
|
4021
|
+
switch (a.kind) {
|
|
4022
|
+
case 'json': {
|
|
4023
|
+
const bytes = utf8ByteLength(safeStringify(a.json));
|
|
4024
|
+
if (bytes > eff.maxAttachmentJsonBytes) {
|
|
4025
|
+
return {
|
|
4026
|
+
ok: false,
|
|
4027
|
+
reason: 'attachment_json_too_large',
|
|
4028
|
+
fieldPath: `${path}.json`,
|
|
4029
|
+
actualBytes: bytes,
|
|
4030
|
+
limitBytes: eff.maxAttachmentJsonBytes,
|
|
4031
|
+
};
|
|
4032
|
+
}
|
|
4033
|
+
break;
|
|
4034
|
+
}
|
|
4035
|
+
case 'text': {
|
|
4036
|
+
const bytes = utf8ByteLength(a.text ?? '');
|
|
4037
|
+
if (bytes > eff.maxAttachmentTextBytes) {
|
|
4038
|
+
return {
|
|
4039
|
+
ok: false,
|
|
4040
|
+
reason: 'attachment_text_too_large',
|
|
4041
|
+
fieldPath: `${path}.text`,
|
|
4042
|
+
actualBytes: bytes,
|
|
4043
|
+
limitBytes: eff.maxAttachmentTextBytes,
|
|
4044
|
+
};
|
|
4045
|
+
}
|
|
4046
|
+
break;
|
|
4047
|
+
}
|
|
4048
|
+
case 'image':
|
|
4049
|
+
case 'file': {
|
|
4050
|
+
const bytes = utf8ByteLength(a.dataUrl ?? '');
|
|
4051
|
+
if (bytes > eff.maxAttachmentDataUrlBytes) {
|
|
4052
|
+
return {
|
|
4053
|
+
ok: false,
|
|
4054
|
+
reason: 'attachment_data_url_too_large',
|
|
4055
|
+
fieldPath: `${path}.dataUrl`,
|
|
4056
|
+
actualBytes: bytes,
|
|
4057
|
+
limitBytes: eff.maxAttachmentDataUrlBytes,
|
|
4058
|
+
};
|
|
4059
|
+
}
|
|
4060
|
+
break;
|
|
4061
|
+
}
|
|
4062
|
+
default:
|
|
4063
|
+
return {
|
|
4064
|
+
ok: false,
|
|
4065
|
+
reason: 'attachment_invalid_kind',
|
|
4066
|
+
fieldPath: `${path}.kind`,
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
return null;
|
|
4071
|
+
}
|
|
4072
|
+
/**
|
|
4073
|
+
* Validate `deepLinkRoute` — byte cap + minimal shape sanity check.
|
|
4074
|
+
*
|
|
4075
|
+
* The DS package is shell-agnostic, so full route grammar validation
|
|
4076
|
+
* (scheme prefixes, `..` segments, `//` runs) lives in the shell's
|
|
4077
|
+
* `DeepLinkService.isValidRoute` — the launcher is the authoritative
|
|
4078
|
+
* gate. Here we just enforce:
|
|
4079
|
+
* - byte cap so a malicious payload can't bloat the JSON
|
|
4080
|
+
* - must start with `/` so the value matches the launcher's contract
|
|
4081
|
+
* and a renderer can safely concatenate / display it.
|
|
4082
|
+
*
|
|
4083
|
+
* Anything more would force the DS to grow a second copy of the route
|
|
4084
|
+
* grammar; the launcher rejects malformed routes at click time so the
|
|
4085
|
+
* worst-case impact of a bad route landing here is a no-op click.
|
|
4086
|
+
*/
|
|
4087
|
+
function checkDeepLinkRoute(payload, eff) {
|
|
4088
|
+
const route = payload.deepLinkRoute;
|
|
4089
|
+
if (route === undefined)
|
|
4090
|
+
return null;
|
|
4091
|
+
if (typeof route !== 'string')
|
|
4092
|
+
return null; // structural; ts catches at the call site
|
|
4093
|
+
if (!route.startsWith('/')) {
|
|
4094
|
+
return {
|
|
4095
|
+
ok: false,
|
|
4096
|
+
reason: 'deep_link_route_invalid_shape',
|
|
4097
|
+
fieldPath: 'deepLinkRoute',
|
|
4098
|
+
};
|
|
4099
|
+
}
|
|
4100
|
+
const bytes = utf8ByteLength(route);
|
|
4101
|
+
if (bytes > eff.maxDeepLinkRouteBytes) {
|
|
4102
|
+
return {
|
|
4103
|
+
ok: false,
|
|
4104
|
+
reason: 'deep_link_route_too_large',
|
|
4105
|
+
fieldPath: 'deepLinkRoute',
|
|
4106
|
+
actualBytes: bytes,
|
|
4107
|
+
limitBytes: eff.maxDeepLinkRouteBytes,
|
|
4108
|
+
};
|
|
4109
|
+
}
|
|
4110
|
+
return null;
|
|
4111
|
+
}
|
|
4112
|
+
function checkMcpScope(payload, eff) {
|
|
4113
|
+
const scope = payload.mcpScope;
|
|
4114
|
+
if (!scope)
|
|
4115
|
+
return null;
|
|
4116
|
+
const apis = scope.apis ?? [];
|
|
4117
|
+
if (apis.length > eff.maxMcpScopeApis) {
|
|
4118
|
+
return {
|
|
4119
|
+
ok: false,
|
|
4120
|
+
reason: 'mcp_scope_too_many_apis',
|
|
4121
|
+
fieldPath: 'mcpScope.apis',
|
|
4122
|
+
actualBytes: apis.length,
|
|
4123
|
+
limitBytes: eff.maxMcpScopeApis,
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
for (let i = 0; i < apis.length; i++) {
|
|
4127
|
+
const bytes = utf8ByteLength(apis[i]);
|
|
4128
|
+
if (bytes > eff.maxMcpScopeApiBytes) {
|
|
4129
|
+
return {
|
|
4130
|
+
ok: false,
|
|
4131
|
+
reason: 'mcp_scope_api_too_large',
|
|
4132
|
+
fieldPath: `mcpScope.apis[${i}]`,
|
|
4133
|
+
actualBytes: bytes,
|
|
4134
|
+
limitBytes: eff.maxMcpScopeApiBytes,
|
|
4135
|
+
};
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
return null;
|
|
4139
|
+
}
|
|
3714
4140
|
/**
|
|
3715
4141
|
* Returns a NEW payload with all string fields trimmed to fit the per-field cap, the
|
|
3716
4142
|
* `plainTextFallback` trimmed to its cap, and the full envelope re-checked against the
|
|
@@ -3816,6 +4242,234 @@ function safeStringify(value) {
|
|
|
3816
4242
|
});
|
|
3817
4243
|
}
|
|
3818
4244
|
|
|
4245
|
+
/**
|
|
4246
|
+
* Imperative sibling to {@link AgentCommandRegistry} / {@link AgentDropRegistry}.
|
|
4247
|
+
*
|
|
4248
|
+
* Apps call {@link dispatch} to push a typed {@link AgentAction} onto the bus;
|
|
4249
|
+
* the agent panel subscribes once at construct and routes by verb. The bus
|
|
4250
|
+
* itself is a thin pass-through — it does NOT decide UI behaviour. The
|
|
4251
|
+
* subscriber (agent-panel) owns: showing the panel, staging the chip,
|
|
4252
|
+
* triggering the flight animation, and binding the command. This keeps the
|
|
4253
|
+
* DS free of host policy.
|
|
4254
|
+
*
|
|
4255
|
+
* Federation-safe: `providedIn: 'root'` + `sharedMappings: ['@mohamedatia/fly-design-system']`
|
|
4256
|
+
* give every federated remote the same singleton, so a remote's "Explain"
|
|
4257
|
+
* button reaches the host's panel without any cross-bundle wiring.
|
|
4258
|
+
*
|
|
4259
|
+
* Validation runs synchronously inside `dispatch` so a caller that sends an
|
|
4260
|
+
* oversize payload sees the throw at their site, not on the subscriber. The
|
|
4261
|
+
* subscriber therefore never has to defend against malformed envelopes.
|
|
4262
|
+
*/
|
|
4263
|
+
class AgentActionBus {
|
|
4264
|
+
_actions$ = new Subject();
|
|
4265
|
+
/** Hot stream of actions in dispatch order. Subscribers receive only
|
|
4266
|
+
* actions dispatched AFTER they subscribe — late subscribers see nothing
|
|
4267
|
+
* retroactively. Use {@link lastAction} for the latest snapshot. */
|
|
4268
|
+
actions$ = this._actions$.asObservable();
|
|
4269
|
+
/** Most recent action — for DevTools, smoke tests, and late-subscriber
|
|
4270
|
+
* catch-up. Null until the first successful dispatch. */
|
|
4271
|
+
lastAction = signal(null, ...(ngDevMode ? [{ debugName: "lastAction" }] : /* istanbul ignore next */ []));
|
|
4272
|
+
/**
|
|
4273
|
+
* The action currently being processed by the subscriber, or null when
|
|
4274
|
+
* none. Set by {@link dispatch} immediately before emitting on
|
|
4275
|
+
* {@link actions$}; cleared by the subscriber via {@link settle} once
|
|
4276
|
+
* it finishes its handler (success or fail). Lets the dispatcher render
|
|
4277
|
+
* a busy state on the originating control — e.g. a card swapping its
|
|
4278
|
+
* sparkle icon for a spinner while the agent panel mints the optimistic
|
|
4279
|
+
* thread and starts the request. Identity check (`bus.inFlight() === act`)
|
|
4280
|
+
* is the panel-side contract; dispatchers usually project to a stable id
|
|
4281
|
+
* inside the payload (e.g. <c>reportId</c>) to scope busy-state visually.
|
|
4282
|
+
*
|
|
4283
|
+
* If multiple dispatches race, the latest wins — the prior in-flight
|
|
4284
|
+
* action is dropped on the floor here (the panel may still handle it,
|
|
4285
|
+
* but the dispatcher's busy indicator follows the newer action). Apps
|
|
4286
|
+
* that need stricter single-flight semantics should guard at the call
|
|
4287
|
+
* site (the agent-panel's <c>_pendingTempThreadId</c> already does so
|
|
4288
|
+
* for the explain verb).
|
|
4289
|
+
*/
|
|
4290
|
+
inFlight = signal(null, ...(ngDevMode ? [{ debugName: "inFlight" }] : /* istanbul ignore next */ []));
|
|
4291
|
+
/**
|
|
4292
|
+
* Push an action onto the bus.
|
|
4293
|
+
*
|
|
4294
|
+
* Throws synchronously when:
|
|
4295
|
+
* - `dispatch === 'auto'` (not implemented in this DS version) — see
|
|
4296
|
+
* {@link AgentActionUnsupportedDispatchError}.
|
|
4297
|
+
* - the payload fails {@link validateAgentPayload} (oversize, invalid
|
|
4298
|
+
* version, invalid kind). The error message carries the field path
|
|
4299
|
+
* so the caller can fix the offending field.
|
|
4300
|
+
*
|
|
4301
|
+
* Subscribers see the action via {@link actions$} on the next tick of
|
|
4302
|
+
* the Subject; the {@link lastAction} signal updates synchronously
|
|
4303
|
+
* before the Subject emits so an effect reading both stays consistent.
|
|
4304
|
+
*/
|
|
4305
|
+
dispatch(action) {
|
|
4306
|
+
if (action.dispatch === 'auto') {
|
|
4307
|
+
throw new AgentActionUnsupportedDispatchError(action.dispatch);
|
|
4308
|
+
}
|
|
4309
|
+
const v = validateAgentPayload(action.payload);
|
|
4310
|
+
if (!v.ok) {
|
|
4311
|
+
const where = v.fieldPath ? ` at ${v.fieldPath}` : '';
|
|
4312
|
+
throw new Error(`AgentActionBus.dispatch: invalid payload — ${v.reason}${where}`);
|
|
4313
|
+
}
|
|
4314
|
+
const a = action;
|
|
4315
|
+
this.lastAction.set(a);
|
|
4316
|
+
this.inFlight.set(a);
|
|
4317
|
+
this._actions$.next(a);
|
|
4318
|
+
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Subscriber contract: call after the handler for {@link inFlight}
|
|
4321
|
+
* completes (success or fail). Only clears {@link inFlight} if it still
|
|
4322
|
+
* points at the passed action — a no-op when a later dispatch already
|
|
4323
|
+
* superseded it. Pass the same action reference the subscriber received
|
|
4324
|
+
* from {@link actions$}; identity is the gate.
|
|
4325
|
+
*/
|
|
4326
|
+
settle(action) {
|
|
4327
|
+
if (this.inFlight() === action) {
|
|
4328
|
+
this.inFlight.set(null);
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
/**
|
|
4332
|
+
* Semantic alias of {@link settle} for explicit user-driven cancellation —
|
|
4333
|
+
* e.g. a future "Stop" button in the agent input tray, or a dispatcher
|
|
4334
|
+
* teardown that wants to abandon its own in-flight action. Identical
|
|
4335
|
+
* runtime behaviour (identity check + clear), but the two-method surface
|
|
4336
|
+
* lets the UI distinguish "handler finished" from "user said no" in
|
|
4337
|
+
* telemetry / logs without sniffing a "reason" parameter.
|
|
4338
|
+
*
|
|
4339
|
+
* Pass the same action reference returned from {@link inFlight} or held
|
|
4340
|
+
* by the dispatcher; identity is the gate.
|
|
4341
|
+
*/
|
|
4342
|
+
cancel(action) {
|
|
4343
|
+
if (this.inFlight() === action) {
|
|
4344
|
+
this.inFlight.set(null);
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentActionBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4348
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentActionBus, providedIn: 'root' });
|
|
4349
|
+
}
|
|
4350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentActionBus, decorators: [{
|
|
4351
|
+
type: Injectable,
|
|
4352
|
+
args: [{ providedIn: 'root' }]
|
|
4353
|
+
}] });
|
|
4354
|
+
|
|
4355
|
+
/**
|
|
4356
|
+
* FLIP-style entry animation for payloads landing in the agent panel.
|
|
4357
|
+
*
|
|
4358
|
+
* Pure DOM + Web Animations API — no Chart.js, no Angular animations module,
|
|
4359
|
+
* no CSS transitions racing layout. Honours `prefers-reduced-motion`: the
|
|
4360
|
+
* ghost is appended then removed without animating when the user asked for
|
|
4361
|
+
* less motion (so DOM side-effects stay consistent).
|
|
4362
|
+
*
|
|
4363
|
+
* Lifecycle:
|
|
4364
|
+
* 1. The agent panel calls {@link registerTarget} in `ngAfterViewInit`
|
|
4365
|
+
* with its header element.
|
|
4366
|
+
* 2. A source app dispatches an `AgentAction` carrying an `originRect`
|
|
4367
|
+
* from `getBoundingClientRect()` on the click target.
|
|
4368
|
+
* 3. The bus subscriber calls {@link flyInto} with that rect.
|
|
4369
|
+
* 4. The animator creates a fixed-position ghost at the origin, animates
|
|
4370
|
+
* transform + opacity toward the registered target's rect, then
|
|
4371
|
+
* removes itself on `onfinish` / `oncancel`.
|
|
4372
|
+
*
|
|
4373
|
+
* Uses `getBoundingClientRect()` (physical viewport coords) so the animation
|
|
4374
|
+
* is RTL-correct without inset-inline math — the rect already encodes the
|
|
4375
|
+
* physical position regardless of `dir`.
|
|
4376
|
+
*
|
|
4377
|
+
* The 900 ms duration and easing curve are deliberately hardcoded — making
|
|
4378
|
+
* them configurable surfaces an API the host can't usefully tune without
|
|
4379
|
+
* understanding motion design as a whole.
|
|
4380
|
+
*/
|
|
4381
|
+
class AgentFlightAnimator {
|
|
4382
|
+
/** Hardcoded — see class doc. */
|
|
4383
|
+
static DURATION_MS = 900;
|
|
4384
|
+
static EASING = 'cubic-bezier(.2,.7,.2,1)';
|
|
4385
|
+
/** Floor the target/source scale ratio so a tiny target rect doesn't
|
|
4386
|
+
* collapse the ghost to invisibility before the animation finishes. */
|
|
4387
|
+
static MIN_SCALE = 0.05;
|
|
4388
|
+
targetEl = null;
|
|
4389
|
+
/** Called by the panel host to publish where flights should land. Pass
|
|
4390
|
+
* `null` on destroy so a re-mounted panel doesn't leave the animator
|
|
4391
|
+
* pointing at a detached node. */
|
|
4392
|
+
registerTarget(el) {
|
|
4393
|
+
this.targetEl = el;
|
|
4394
|
+
}
|
|
4395
|
+
/**
|
|
4396
|
+
* Animate a ghost element from {@link from} to the registered target's
|
|
4397
|
+
* rect. No-ops when:
|
|
4398
|
+
* - no target is registered (silent — panel may not be mounted yet)
|
|
4399
|
+
* - running outside a browser (SSR safety)
|
|
4400
|
+
* - the user has `prefers-reduced-motion: reduce` set (DOM is still
|
|
4401
|
+
* touched so callers see consistent side-effects, but no animation
|
|
4402
|
+
* runs)
|
|
4403
|
+
*
|
|
4404
|
+
* The ghost is appended to `document.body` (not the panel) so a parent
|
|
4405
|
+
* `overflow: hidden` on the panel can't clip the flight path.
|
|
4406
|
+
*/
|
|
4407
|
+
flyInto(from, opts = {}) {
|
|
4408
|
+
if (!this.targetEl)
|
|
4409
|
+
return;
|
|
4410
|
+
if (typeof document === 'undefined')
|
|
4411
|
+
return;
|
|
4412
|
+
const reduced = typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
|
4413
|
+
? window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
4414
|
+
: false;
|
|
4415
|
+
const to = this.targetEl.getBoundingClientRect();
|
|
4416
|
+
const ghost = document.createElement('div');
|
|
4417
|
+
ghost.className = 'fly-agent-flight-ghost';
|
|
4418
|
+
// The ghost is purely decorative — it duplicates the source content
|
|
4419
|
+
// (chart title + snapshot) that the user just clicked and screen-reader
|
|
4420
|
+
// users have already heard via the sparkle button's aria-label. Re-
|
|
4421
|
+
// announcing the same content during the flight would be noisy at best
|
|
4422
|
+
// and confusing at worst. aria-hidden hides the subtree from assistive
|
|
4423
|
+
// tech while leaving it visible for sighted users.
|
|
4424
|
+
ghost.setAttribute('aria-hidden', 'true');
|
|
4425
|
+
// role="presentation" is belt-and-braces — even if a future renderer
|
|
4426
|
+
// walks the tree looking for semantic landmarks, the ghost has none.
|
|
4427
|
+
ghost.setAttribute('role', 'presentation');
|
|
4428
|
+
if (opts.previewHtml) {
|
|
4429
|
+
// Caller is responsible for safe HTML — the bus subscriber escapes
|
|
4430
|
+
// plainTextFallback before passing it here.
|
|
4431
|
+
ghost.innerHTML = opts.previewHtml;
|
|
4432
|
+
}
|
|
4433
|
+
Object.assign(ghost.style, {
|
|
4434
|
+
position: 'fixed',
|
|
4435
|
+
left: `${from.left}px`,
|
|
4436
|
+
top: `${from.top}px`,
|
|
4437
|
+
width: `${from.width}px`,
|
|
4438
|
+
height: `${from.height}px`,
|
|
4439
|
+
pointerEvents: 'none',
|
|
4440
|
+
zIndex: '99999',
|
|
4441
|
+
transformOrigin: 'top left',
|
|
4442
|
+
willChange: 'transform, opacity',
|
|
4443
|
+
});
|
|
4444
|
+
document.body.appendChild(ghost);
|
|
4445
|
+
if (reduced) {
|
|
4446
|
+
ghost.remove();
|
|
4447
|
+
return;
|
|
4448
|
+
}
|
|
4449
|
+
const dx = to.left - from.left;
|
|
4450
|
+
const dy = to.top - from.top;
|
|
4451
|
+
const sx = Math.max(to.width / from.width, AgentFlightAnimator.MIN_SCALE);
|
|
4452
|
+
const sy = Math.max(to.height / from.height, AgentFlightAnimator.MIN_SCALE);
|
|
4453
|
+
const anim = ghost.animate([
|
|
4454
|
+
{ transform: 'translate(0,0) scale(1,1)', opacity: 1 },
|
|
4455
|
+
{ transform: `translate(${dx}px,${dy}px) scale(${sx},${sy})`, opacity: 0.15 },
|
|
4456
|
+
], {
|
|
4457
|
+
duration: AgentFlightAnimator.DURATION_MS,
|
|
4458
|
+
easing: AgentFlightAnimator.EASING,
|
|
4459
|
+
fill: 'forwards',
|
|
4460
|
+
});
|
|
4461
|
+
const cleanup = () => ghost.remove();
|
|
4462
|
+
anim.onfinish = cleanup;
|
|
4463
|
+
anim.oncancel = cleanup;
|
|
4464
|
+
}
|
|
4465
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentFlightAnimator, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4466
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentFlightAnimator, providedIn: 'root' });
|
|
4467
|
+
}
|
|
4468
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentFlightAnimator, decorators: [{
|
|
4469
|
+
type: Injectable,
|
|
4470
|
+
args: [{ providedIn: 'root' }]
|
|
4471
|
+
}] });
|
|
4472
|
+
|
|
3819
4473
|
/**
|
|
3820
4474
|
* Strict whitelist for the ghost element id. Letter-led ASCII, allows word/hyphen/colon/dot,
|
|
3821
4475
|
* capped at 128 chars. Anything else is rejected before reaching `getElementById` so a
|
|
@@ -4640,6 +5294,31 @@ function isRtlLocaleEntry(entry) {
|
|
|
4640
5294
|
* centralising the canonicalization rule that strips `-service` from
|
|
4641
5295
|
* JWT client_ids (`circles-service` → `circles`) so chips, banners,
|
|
4642
5296
|
* and launchers stay consistent across the shell.
|
|
5297
|
+
* v2.6.0: Agent action bus + flight animator. New imperative sibling to
|
|
5298
|
+
* `AgentCommandRegistry` / `AgentDropRegistry`: `AgentActionBus` lets
|
|
5299
|
+
* any app push a typed `AgentAction { verb, payload, dispatch, originRect }`
|
|
5300
|
+
* to the agent panel without going through drag-drop. `AgentFlightAnimator`
|
|
5301
|
+
* plays a FLIP-style transform animation from the source DOM rect to the
|
|
5302
|
+
* registered panel-header rect (hardcoded 420 ms / cubic-bezier; honours
|
|
5303
|
+
* prefers-reduced-motion). Phase 1 supports `dispatch: 'stage'` only —
|
|
5304
|
+
* `'auto'` throws `AgentActionUnsupportedDispatchError` until
|
|
5305
|
+
* `AgentInputComponent.programmaticSubmit` ships. Re-uses
|
|
5306
|
+
* `AgentDragPayload<T>` as the wire envelope so drag and programmatic
|
|
5307
|
+
* transports never fork. New verbs: `explain`, `why-empty`,
|
|
5308
|
+
* `compose-query`, `compare`, `forecast`, `summarize`.
|
|
5309
|
+
* v2.10.0: Per-window help-deeplink contract. The shell renders a help-icon
|
|
5310
|
+
* button in every non-chromeless `<fly-window>` titlebar. By default
|
|
5311
|
+
* the deeplink uses `win.appId`; apps override or augment via the
|
|
5312
|
+
* new `WINDOW_HELP_HINT` InjectionToken (a `WritableSignal<WindowHelpHint | null>`)
|
|
5313
|
+
* provided per-window. `WindowHelpHint` is `{ appId?, topic? }` —
|
|
5314
|
+
* `topic` is a free-form search-query seed updated dynamically as the
|
|
5315
|
+
* user navigates within the app. New federation-safe channel
|
|
5316
|
+
* `FLY_WINDOW_HELP_HINT_EVENT` mirrors the DI surface for federated
|
|
5317
|
+
* remotes. New exports: `WindowHelpHint`, `WINDOW_HELP_HINT`,
|
|
5318
|
+
* `FLY_WINDOW_HELP_HINT_EVENT`, `FlyWindowHelpHintEventDetail`.
|
|
5319
|
+
* Backed by the help-center reader's new `params.topic` launch payload
|
|
5320
|
+
* and "no help for this app yet" empty-state. See
|
|
5321
|
+
* `skills/help-center.md` and `skills/desktop-shell-angular.md`.
|
|
4643
5322
|
* v2.5.1: New `MessageBoxService.showAcknowledged()` entry point returning
|
|
4644
5323
|
* `DialogResultWithAcknowledgement` for "Don't ask again"-style flows.
|
|
4645
5324
|
* The `dontAskAgain` config (`MessageBoxDontAskAgainConfig`) lives ONLY on
|
|
@@ -4667,5 +5346,5 @@ const AUDIENCE_ERROR_CODES = {
|
|
|
4667
5346
|
* Generated bundle index. Do not edit.
|
|
4668
5347
|
*/
|
|
4669
5348
|
|
|
4670
|
-
export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, APP_LOOKUP, AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AgentCommandRegistry, AgentDropRegistry, AgentPayloadOversizeError, AudienceBuilderComponent, AuthService, ContextMenuComponent, DEFAULT_AGENT_PAYLOAD_LIMITS, DEFAULT_FLY_THEME_MODE, DialogResult, FLYOS_LAUNCH_EVENT, FLY_LOCALE_CATALOG, FLY_REMOTE_BASE_PATH, FLY_REMOTE_ROUTES, FLY_THEME_MODE_IDS, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlySecureSrcDirective, FlyThemeService, FlyosPendingLaunchesGlobalKey, I18nService, LAUNCH_CONTEXT, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, RTL_LOCALE_SET, SHARE_ORG_CHART_SYSTEM_KEY_APPS, SHARE_ORG_CHART_SYSTEM_KEY_DEFAULT, SHARE_PANEL_DEFAULT_FILE_LEVELS, SharePanelComponent, SourceAppResolver, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, loadRemoteStyles, matchFlyRoutePattern, normalizeFlyTheme, trimAgentPayload, trimAgentString, unloadRemoteStyles, utf8ByteLength, validateAgentPayload };
|
|
5349
|
+
export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, APP_LOOKUP, AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AgentActionBus, AgentActionUnsupportedDispatchError, AgentCommandRegistry, AgentDropRegistry, AgentFlightAnimator, AgentLookupRegistry, AgentPayloadOversizeError, AudienceBuilderComponent, AuthService, ContextMenuComponent, DEFAULT_AGENT_PAYLOAD_LIMITS, DEFAULT_FLY_THEME_MODE, DialogResult, FLYOS_LAUNCH_EVENT, FLY_LOCALE_CATALOG, FLY_REMOTE_BASE_PATH, FLY_REMOTE_ROUTES, FLY_THEME_MODE_IDS, FLY_WINDOW_HELP_HINT_EVENT, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyRemoteRouterOutletComponent, FlySecureSrcDirective, FlyThemeService, FlyWindowHelpService, FlyosPendingLaunchesGlobalKey, I18nService, LAUNCH_CONTEXT, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, RTL_LOCALE_SET, SHARE_ORG_CHART_SYSTEM_KEY_APPS, SHARE_ORG_CHART_SYSTEM_KEY_DEFAULT, SHARE_PANEL_DEFAULT_FILE_LEVELS, SUPPORTED_AGENT_PAYLOAD_VERSIONS, SharePanelComponent, SourceAppResolver, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WINDOW_HELP_HINT, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, loadRemoteStyles, matchFlyRoutePattern, normalizeFlyTheme, trimAgentPayload, trimAgentString, unloadRemoteStyles, utf8ByteLength, validateAgentPayload };
|
|
4671
5350
|
//# sourceMappingURL=mohamedatia-fly-design-system.mjs.map
|