@mohamedatia/fly-design-system 2.4.0 → 2.6.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.
@@ -2,6 +2,8 @@ import * as _angular_core from '@angular/core';
2
2
  import { Type, InjectionToken, Signal, OnInit, EventEmitter, OnChanges, OnDestroy, SimpleChanges, PipeTransform, signal, AfterViewInit, ElementRef } from '@angular/core';
3
3
  import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
4
4
  import { Observable } from 'rxjs';
5
+ import { NavigationExtras } from '@angular/router';
6
+ import * as _mohamedatia_fly_design_system from '@mohamedatia/fly-design-system';
5
7
 
6
8
  /**
7
9
  * FlyOS architectural app category — see repo `docs/01-ecosystem-overview.md`.
@@ -880,6 +882,105 @@ declare class StandaloneWindowManagerService extends WindowManagerService {
880
882
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<StandaloneWindowManagerService>;
881
883
  }
882
884
 
885
+ /**
886
+ * FlyOS standard navigation surface for Business / Supporting App remotes.
887
+ *
888
+ * Problem this solves
889
+ * -------------------
890
+ * A federated remote ships with its own Angular `Routes` table that works
891
+ * perfectly in **standalone** mode (e.g. `http://localhost:7202/signals/:id`).
892
+ * Once the same remote is mounted inside the FlyOS desktop shell via
893
+ * `*ngComponentOutlet`, `inject(Router)` resolves to the **host shell's**
894
+ * Router — which has no knowledge of `/signals/:id`. Calls like
895
+ * `router.navigate(['/signals', id])` then silently no-op and the click
896
+ * "does nothing."
897
+ *
898
+ * The fix
899
+ * -------
900
+ * Inject `FlyRemoteRouter` instead of `Router`. It mirrors `Router.navigate` /
901
+ * `Router.navigateByUrl` and:
902
+ *
903
+ * - **Standalone** (no `WINDOW_DATA`): delegates to the real Angular Router,
904
+ * so the URL bar, browser back button, and `RouterLink` continue to work
905
+ * exactly as before.
906
+ * - **Embedded** (mounted in the shell via `WINDOW_DATA`): writes the target
907
+ * URL to an internal signal (`url`, `segments`) and does **not** touch the
908
+ * host's Router. The remote's root component watches the signal and renders
909
+ * the right view (typically a `@switch` on the first segment, plus an inline
910
+ * overlay for detail pages).
911
+ *
912
+ * Usage in a list page
913
+ * --------------------
914
+ * ```ts
915
+ * private readonly router = inject(FlyRemoteRouter);
916
+ *
917
+ * openDetail(item: Signal): void {
918
+ * this.router.navigate(['/signals', item.id]); // works in both modes
919
+ * }
920
+ * ```
921
+ *
922
+ * Usage in the remote's root component (embedded dispatcher)
923
+ * ----------------------------------------------------------
924
+ * ```ts
925
+ * private readonly router = inject(FlyRemoteRouter);
926
+ *
927
+ * readonly view = computed(() => {
928
+ * if (!this.router.isEmbedded) return null; // standalone: <router-outlet> handles it
929
+ * const [head, id] = this.router.segments();
930
+ * if (head === 'signals' && id) return { kind: 'signal-detail', id };
931
+ * if (head === 'signals') return { kind: 'signals-list' };
932
+ * return { kind: head ?? 'home' };
933
+ * });
934
+ * ```
935
+ *
936
+ * Notes
937
+ * -----
938
+ * - `WINDOW_DATA` is the canonical "I am running inside the shell" marker; the
939
+ * shell provides it per window, and it is `null` everywhere else.
940
+ * - `Router` is injected as `optional`. A remote that does not register a Router
941
+ * (e.g. a tiny chromeless utility app) still gets a working `navigate` that
942
+ * updates the `url` signal in embedded mode.
943
+ */
944
+ declare class FlyRemoteRouter {
945
+ private readonly windowData;
946
+ private readonly router;
947
+ /** True when this remote is rendered inside the FlyOS desktop shell. */
948
+ readonly isEmbedded: boolean;
949
+ private readonly _url;
950
+ /**
951
+ * Current logical URL of the remote — `/signals/abc` etc.
952
+ *
953
+ * Standalone: mirrors `Router.url` and is updated on every `NavigationEnd`.
954
+ * Embedded: updated by `navigate` / `navigateByUrl` only.
955
+ */
956
+ readonly url: _angular_core.Signal<string>;
957
+ /**
958
+ * `url` parsed into path segments, e.g. `'/signals/abc'` → `['signals', 'abc']`.
959
+ * Query string and hash are stripped. Empty segments removed.
960
+ */
961
+ readonly segments: _angular_core.Signal<readonly string[]>;
962
+ constructor();
963
+ /**
964
+ * Navigate to a route, identified by an array of commands as you would pass
965
+ * to Angular's `Router.navigate`. In embedded mode `extras` is ignored — the
966
+ * shell owns history and query-param handling for the window's URL.
967
+ */
968
+ navigate(commands: readonly unknown[], extras?: NavigationExtras): Promise<boolean>;
969
+ /**
970
+ * Navigate to a route given as a full URL string. Mirrors `Router.navigateByUrl`.
971
+ */
972
+ navigateByUrl(url: string, extras?: NavigationExtras): Promise<boolean>;
973
+ /**
974
+ * Convenience: pop the last two segments (the typical "go back from detail
975
+ * to list" gesture). In standalone mode this falls through to `history.back()`
976
+ * so the browser's back stack is respected.
977
+ */
978
+ back(): void;
979
+ private buildUrl;
980
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FlyRemoteRouter, never>;
981
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<FlyRemoteRouter>;
982
+ }
983
+
883
984
  /**
884
985
  * Translates a key using the shared I18nService.
885
986
  *
@@ -1035,11 +1136,37 @@ interface MessageBoxOptions {
1035
1136
  buttons?: MessageBoxButtons;
1036
1137
  icon?: MessageBoxIcon;
1037
1138
  }
1139
+ /**
1140
+ * Configuration for the "Don't ask again" checkbox rendered by
1141
+ * `showAcknowledged()`. The field is intentionally NOT part of the base
1142
+ * `MessageBoxOptions` so the type system enforces the rule
1143
+ * "checkbox ⇒ caller MUST use `showAcknowledged()`": the classic `show()`
1144
+ * entry point cannot accept this shape, eliminating the silent-discard
1145
+ * dark-pattern path where the user's choice was thrown away.
1146
+ */
1147
+ interface MessageBoxDontAskAgainConfig {
1148
+ /** i18n key for the checkbox label, e.g. 'agent.command.builtin.clear.dont_ask_again'. */
1149
+ labelKey: string;
1150
+ /** Initial checked state. Default false. */
1151
+ defaultChecked?: boolean;
1152
+ }
1153
+ /**
1154
+ * Options narrowed to those carrying a `dontAskAgain` config — the only shape
1155
+ * accepted by `showAcknowledged()`.
1156
+ */
1157
+ interface MessageBoxOptionsWithAcknowledgement extends MessageBoxOptions {
1158
+ dontAskAgain: MessageBoxDontAskAgainConfig;
1159
+ }
1038
1160
  interface MessageBoxButton {
1039
1161
  label: string;
1040
1162
  result: DialogResult;
1041
1163
  variant?: 'primary' | 'danger' | 'default';
1042
1164
  }
1165
+ /** Shape returned by `showAcknowledged()` — pairs the dialog result with the checkbox state. */
1166
+ interface DialogResultWithAcknowledgement {
1167
+ readonly result: DialogResult;
1168
+ readonly dontAskAgain: boolean;
1169
+ }
1043
1170
  declare class MessageBoxService {
1044
1171
  private readonly i18n;
1045
1172
  readonly visible: _angular_core.WritableSignal<boolean>;
@@ -1047,9 +1174,32 @@ declare class MessageBoxService {
1047
1174
  readonly message: _angular_core.WritableSignal<string>;
1048
1175
  readonly icon: _angular_core.WritableSignal<MessageBoxIcon>;
1049
1176
  readonly buttons: _angular_core.WritableSignal<MessageBoxButton[]>;
1177
+ /**
1178
+ * Active "Don't ask again" config — populated only by `showAcknowledged()`.
1179
+ * `show()` callers cannot reach this signal because the type system rejects
1180
+ * `dontAskAgain` on the base `MessageBoxOptions`.
1181
+ */
1182
+ readonly dontAskAgain: _angular_core.WritableSignal<MessageBoxDontAskAgainConfig | undefined>;
1183
+ /**
1184
+ * Component-side checkbox state. The component (`MessageBoxComponent`) wires
1185
+ * its own signal to this one; the service explicitly re-seeds it on every
1186
+ * `_open()` so reusing a constant `options` literal across two opens does
1187
+ * NOT leak the previous user's checkbox state through signal `===` dedupe.
1188
+ * Exposed for the component to read/write; not for general callers.
1189
+ */
1190
+ readonly dontAskAgainChecked: _angular_core.WritableSignal<boolean>;
1050
1191
  private resolver;
1051
1192
  show(options: MessageBoxOptions): Promise<DialogResult>;
1052
- resolve(result: DialogResult): void;
1193
+ /**
1194
+ * Opens a message box with a "Don't ask again" checkbox and resolves to both
1195
+ * the dialog result and the checkbox state. Throws when `options.dontAskAgain`
1196
+ * is missing — the checkbox config is mandatory for this entry point. The
1197
+ * compile-time signature already guarantees presence; the runtime check is
1198
+ * defense-in-depth for callers that bypass the type system via `as never`.
1199
+ */
1200
+ showAcknowledged(options: MessageBoxOptionsWithAcknowledgement): Promise<DialogResultWithAcknowledgement>;
1201
+ resolve(result: DialogResult, dontAskAgain?: boolean): void;
1202
+ private _open;
1053
1203
  private resolveButtons;
1054
1204
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MessageBoxService, never>;
1055
1205
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<MessageBoxService>;
@@ -1060,11 +1210,25 @@ declare class MessageBoxComponent implements AfterViewInit {
1060
1210
  private elRef;
1061
1211
  readonly MessageBoxIcon: typeof MessageBoxIcon;
1062
1212
  readonly DialogResult: typeof DialogResult;
1213
+ /** Mirror of the service's `dontAskAgain` config so the template can read it directly. */
1214
+ readonly dontAskAgainConfig: _angular_core.Signal<_mohamedatia_fly_design_system.MessageBoxDontAskAgainConfig | undefined>;
1215
+ /**
1216
+ * User-driven checkbox state, owned by the service and re-seeded on every
1217
+ * `_open()` so reusing a constant options literal cannot leak prior state.
1218
+ */
1219
+ readonly dontAskAgainChecked: _angular_core.WritableSignal<boolean>;
1220
+ /**
1221
+ * Concatenated id list for the dialog's `aria-describedby`. Always includes
1222
+ * the message; appends the checkbox label id when the checkbox is rendered
1223
+ * so screen readers announce the checkbox as part of the description.
1224
+ */
1225
+ readonly ariaDescribedBy: _angular_core.Signal<"mb-message mb-dont-ask" | "mb-message">;
1063
1226
  private previouslyFocused;
1064
1227
  ngAfterViewInit(): void;
1065
1228
  onEscape(): void;
1066
1229
  onBackdropClick(): void;
1067
1230
  onButtonClick(result: DialogResult): void;
1231
+ onDontAskAgainToggle(ev: Event): void;
1068
1232
  iconClass(): string;
1069
1233
  private focusPrimaryButton;
1070
1234
  private restoreFocus;
@@ -1221,6 +1385,465 @@ declare class FlyFileUploadComponent {
1221
1385
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<FlyFileUploadComponent, "fly-file-upload", never, { "maxFiles": { "alias": "maxFiles"; "required": false; "isSignal": true; }; "maxFileSizeBytes": { "alias": "maxFileSizeBytes"; "required": false; "isSignal": true; }; "accept": { "alias": "accept"; "required": false; "isSignal": true; }; "sourceApp": { "alias": "sourceApp"; "required": false; "isSignal": true; }; "sourceEntityType": { "alias": "sourceEntityType"; "required": false; "isSignal": true; }; "sourceEntityId": { "alias": "sourceEntityId"; "required": false; "isSignal": true; }; "existingFileIds": { "alias": "existingFileIds"; "required": false; "isSignal": true; }; "files": { "alias": "files"; "required": false; "isSignal": true; }; }, { "files": "filesChange"; "filesChanged": "filesChanged"; }, never, never, true, never>;
1222
1386
  }
1223
1387
 
1388
+ /**
1389
+ * Agent input contracts.
1390
+ *
1391
+ * These types define the wire and DI surface for the new `<fly-agent-input>` orchestrator
1392
+ * (Phase 2+). Phase 1 ships only the contracts: payload envelope, command/drop registry
1393
+ * shapes, and chip-host inputs. The concrete components consume them later.
1394
+ *
1395
+ * Design rules:
1396
+ * - All fields are `readonly` — the registries store these in signal stores and we never
1397
+ * want a host mutating after registration. Hosts construct fresh objects to update.
1398
+ * - The discriminated kinds (`scope`, `mode`, `kind`) carry the entire variance — no
1399
+ * boolean flags. New variants extend the union without breaking existing consumers.
1400
+ * - The `Type<AgentChipHostInputs<T>>` shape is intentional: chip components declare
1401
+ * `payload`, `mode`, optional `onRemove` as Angular `input()` signals; the registry
1402
+ * wires them via `NgComponentOutlet` inputs in the consumer. The component reference
1403
+ * itself never leaks across the federation boundary by value — only by class identity.
1404
+ */
1405
+ /** Frozen MIME used by `flyAgentDraggable` and the drop-zone reader. Never change without a DS major. */
1406
+ declare const AGENT_DRAG_MIME: "application/x-fly-agent-payload+json";
1407
+ /** Frozen payload envelope version. Bump only with a published DS major. */
1408
+ declare const AGENT_PAYLOAD_VERSION: 1;
1409
+ /**
1410
+ * Where a command surfaces. `'global'` = always offered. `{ appId }` = offered only when
1411
+ * the host's live `liveAppIds` set (passed to {@link AgentCommandRegistry.visible}) contains
1412
+ * that id. This lets a remote register its slash command at boot but only have it appear
1413
+ * in the palette while at least one of the remote's windows is open.
1414
+ */
1415
+ type AgentCommandScope = 'global' | {
1416
+ readonly appId: string;
1417
+ };
1418
+ /**
1419
+ * A slash command offered by the agent input palette. Commands are deliberately shallow
1420
+ * descriptors — the host (shell) decides what happens on Send. The descriptor's role is
1421
+ * to make the palette row renderable (icon, label, kbd hint, app badge) and matchable
1422
+ * (id, aliases, drop-kind suggestions) without coupling DS to any business logic.
1423
+ */
1424
+ interface AgentCommand {
1425
+ /** Stable id, kebab-case. Becomes the slash text after `/`, e.g. `/analyze-trend`. */
1426
+ readonly id: string;
1427
+ /** i18n key for the visible label, e.g. `agent.command.analyze_trend.label`. */
1428
+ readonly labelKey: string;
1429
+ /** i18n key for an optional sublabel / description shown beneath the label. */
1430
+ readonly descriptionKey?: string;
1431
+ /** PrimeIcons class, e.g. `pi-chart-line`. */
1432
+ readonly icon?: string;
1433
+ /** App badge label key — appears as a small tag, e.g. `agent.command.app_badge.circles`. */
1434
+ readonly appBadgeKey?: string;
1435
+ /** Optional kbd hint i18n key. */
1436
+ readonly kbdHintKey?: string;
1437
+ /** When the command is keyword-matched against drop payload kinds, list those kinds here. */
1438
+ readonly suggestForDropKinds?: readonly string[];
1439
+ /** Extra match terms beyond id + label, used by the fuzzy filter. */
1440
+ readonly aliases?: readonly string[];
1441
+ /** When `'send'`, the command is inserted as a bound prefix and executed on Send. */
1442
+ readonly executeOn: 'send';
1443
+ /** Optional client-side gate. Return false to hide the command in this scope. */
1444
+ readonly isVisible?: () => boolean;
1445
+ }
1446
+ /** What hosts pass to {@link AgentCommandRegistry.register}. Adds the scope to {@link AgentCommand}. */
1447
+ interface AgentCommandRegistration extends AgentCommand {
1448
+ readonly scope: AgentCommandScope;
1449
+ }
1450
+ /** Disposable handle returned by {@link AgentCommandRegistry.register}. Idempotent `dispose()`. */
1451
+ interface AgentCommandHandle {
1452
+ dispose(): void;
1453
+ }
1454
+ /**
1455
+ * The wire envelope written to a `DataTransfer` by `flyAgentDraggable`. Renderers narrow
1456
+ * on `kind`; mismatches fall back to `plainTextFallback`.
1457
+ */
1458
+ interface AgentDragPayload<T = unknown> {
1459
+ /** Domain-stable kind, e.g. `circles.trend`. */
1460
+ readonly kind: string;
1461
+ /** Originating appId. Must match an entry in the host's app registry. */
1462
+ readonly appId: string;
1463
+ /** Frozen format version. Mismatch -> renderer falls back to text. */
1464
+ readonly version: typeof AGENT_PAYLOAD_VERSION;
1465
+ /** Domain object. Renderers narrow on `kind`. */
1466
+ readonly payload: T;
1467
+ /** Plain-text fallback written to the dataTransfer alongside the typed MIME. */
1468
+ readonly plainTextFallback: string;
1469
+ /** Optional command ids to highlight when this payload is dropped. */
1470
+ readonly suggestedCommandIds?: readonly string[];
1471
+ }
1472
+ /**
1473
+ * Where a chip is being rendered. `inline` = inside the input chip tray (removable);
1474
+ * `message` = embedded in a sent message bubble (not removable).
1475
+ */
1476
+ type AgentDropChipMode = 'inline' | 'message';
1477
+ /**
1478
+ * Public input shape for chip-renderer components. Components declare matching
1479
+ * `input()` signals; the registry wires them via `NgComponentOutlet`.
1480
+ */
1481
+ interface AgentChipHostInputs<T = unknown> {
1482
+ payload: AgentDragPayload<T>;
1483
+ mode: AgentDropChipMode;
1484
+ /** Called when the user clicks the chip's remove control (only in inline mode). */
1485
+ onRemove?: () => void;
1486
+ }
1487
+ /**
1488
+ * Keyboard-alternative draggable item. Hosts publish these via
1489
+ * {@link AgentDropRegistry.publishDraggables} so the "Attach from app…" menu can offer
1490
+ * them when pointer drag isn't available.
1491
+ */
1492
+ interface AgentDraggableItem<T = unknown> {
1493
+ /** Stable id within its app. Used as a list key in the kbd menu. */
1494
+ readonly id: string;
1495
+ readonly labelKey: string;
1496
+ readonly icon?: string;
1497
+ /** Builds the payload the moment the user picks the item. */
1498
+ build(): AgentDragPayload<T>;
1499
+ }
1500
+ /** Registry input shape for binding a chip-renderer component to a kind+appId pair. */
1501
+ interface AgentDropRendererRegistration<T = unknown> {
1502
+ readonly kind: string;
1503
+ readonly appId: string;
1504
+ readonly component: Type<AgentChipHostInputs<T>>;
1505
+ }
1506
+ /**
1507
+ * Caps applied by {@link validateAgentPayload} / {@link trimAgentPayload}. The defaults
1508
+ * trade off "fits comfortably in a single MQ frame" against "covers a richly-populated
1509
+ * trend or note card". Hosts may pass a partial override.
1510
+ */
1511
+ interface AgentPayloadLimits {
1512
+ /** Cap on the JSON.stringify(envelope) byte length. Default 32 KB. */
1513
+ readonly maxJsonBytes: number;
1514
+ /** Cap on the plainTextFallback byte length. Default 8 KB. */
1515
+ readonly maxPlainTextFallbackBytes: number;
1516
+ /** Cap on any single string field inside payload (recursive). Default 4 KB. */
1517
+ readonly maxStringFieldBytes: number;
1518
+ }
1519
+ declare const DEFAULT_AGENT_PAYLOAD_LIMITS: AgentPayloadLimits;
1520
+ /**
1521
+ * Stable failure reasons emitted by {@link validateAgentPayload}.
1522
+ * - `invalid_version` — `version` !== {@link AGENT_PAYLOAD_VERSION}.
1523
+ * - `invalid_kind` — `kind` is empty / not a string.
1524
+ * - `field_too_large` — a string field inside `payload` exceeds `maxStringFieldBytes`.
1525
+ * - `fallback_too_large` — `plainTextFallback` exceeds `maxPlainTextFallbackBytes`.
1526
+ * - `json_too_large` — the full JSON.stringify(envelope) exceeds `maxJsonBytes`.
1527
+ */
1528
+ type AgentPayloadValidationResult = {
1529
+ readonly ok: true;
1530
+ } | {
1531
+ readonly ok: false;
1532
+ readonly reason: 'json_too_large' | 'fallback_too_large' | 'field_too_large' | 'invalid_version' | 'invalid_kind';
1533
+ /** Dotted property-access path to the offending field, e.g. `payload.title` or `payload.tags[2]`. NOT RFC 6901 JSON Pointer. */
1534
+ readonly fieldPath?: string;
1535
+ readonly actualBytes?: number;
1536
+ readonly limitBytes?: number;
1537
+ };
1538
+
1539
+ /**
1540
+ * Singleton registry of slash commands offered by the agent input palette.
1541
+ *
1542
+ * Lives in the DS so it crosses the federation boundary as a single instance via
1543
+ * `sharedMappings: ['@mohamedatia/fly-design-system']`. Federated remotes register
1544
+ * their commands at boot (and dispose on window close) without forking the shell.
1545
+ *
1546
+ * Storage is a signal store. `register` is O(1) for the unique-id case and O(n) when
1547
+ * replacing an existing id (filter then push). The append-then-replace strategy is
1548
+ * deliberate: registrations are rare (each one corresponds to a remote's app-init
1549
+ * effect), and the tradeoff buys us a stable id-collision contract — the *latest*
1550
+ * registration wins, and the previous handle's `dispose()` becomes a no-op rather
1551
+ * than removing the new entry.
1552
+ */
1553
+ declare class AgentCommandRegistry {
1554
+ private readonly _commands;
1555
+ /** All currently-registered commands, in insertion order. */
1556
+ readonly all: Signal<readonly AgentCommandRegistration[]>;
1557
+ /**
1558
+ * Returns a signal of commands whose scope is `'global'` OR whose `scope.appId` is in
1559
+ * the live app set. The signal recomputes when either the registry or `liveAppIds`
1560
+ * changes — pass a `Signal<ReadonlySet<string>>` from the host's app-registry service
1561
+ * for reactive filtering.
1562
+ */
1563
+ visible(liveAppIds: ReadonlySet<string> | Signal<ReadonlySet<string>>): Signal<readonly AgentCommandRegistration[]>;
1564
+ /**
1565
+ * Register a single command. Returns a handle whose `dispose()` removes the row by
1566
+ * id. If the same id is later re-registered, the original handle's `dispose()`
1567
+ * becomes a no-op (the newer registration owns the row). Idempotent disposal.
1568
+ */
1569
+ register(cmd: AgentCommandRegistration): AgentCommandHandle;
1570
+ /**
1571
+ * Bulk register. Rolls back on duplicate id within the input batch (throws before any
1572
+ * row lands). Cross-batch duplicates against existing rows follow the standard
1573
+ * "latest wins" rule and do NOT trigger rollback.
1574
+ *
1575
+ * Returns a handle whose `dispose()` tears down every row registered by this call.
1576
+ */
1577
+ registerAll(cmds: readonly AgentCommandRegistration[]): AgentCommandHandle;
1578
+ /** Tear down by id. Idempotent. */
1579
+ unregister(id: string): void;
1580
+ /** Monotonic counter; identifies which registration call currently owns each id. */
1581
+ private _generation;
1582
+ /** id → generation. Used so a stale handle's `dispose()` is a no-op after replacement. */
1583
+ private readonly _owners;
1584
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AgentCommandRegistry, never>;
1585
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<AgentCommandRegistry>;
1586
+ }
1587
+
1588
+ /**
1589
+ * Singleton registry of chip-renderer components and keyboard-alternative draggable
1590
+ * items, keyed by `kind` and `appId`.
1591
+ *
1592
+ * Like {@link AgentCommandRegistry}, this lives in the DS so it crosses the federation
1593
+ * boundary as a single instance. Hosts (the shell `<fly-agent-input>`) lookup
1594
+ * renderers; remotes register them.
1595
+ *
1596
+ * Renderer lookup is `O(n)` over the registered list — the registry is small (one or
1597
+ * two entries per app) and lookups happen on drop, not per frame.
1598
+ *
1599
+ * Draggable storage is per-`appId` writable signal cached in a Map, so each `appId`
1600
+ * gets a stable {@link Signal} reference across reads (callers can `===`-compare).
1601
+ */
1602
+ declare class AgentDropRegistry {
1603
+ private readonly _renderers;
1604
+ /** Per-appId writable store for draggables. Read-only mirror returned to callers. */
1605
+ private readonly _draggablesByApp;
1606
+ /**
1607
+ * Look up the chip-renderer component class for a `kind`. The newest registration for
1608
+ * a given `kind` wins, regardless of `appId` — we scan the list in reverse so a later
1609
+ * `register` call shadows an earlier one for the same `kind`.
1610
+ *
1611
+ * Returns `null` when no renderer is registered; the host falls back to a generic
1612
+ * `plainTextFallback` chip.
1613
+ */
1614
+ rendererFor(kind: string): Type<AgentChipHostInputs> | null;
1615
+ /**
1616
+ * Register a renderer for a `(kind, appId)` pair. Re-registering the same pair
1617
+ * replaces the prior entry; the disposal handle for the prior registration becomes
1618
+ * a no-op.
1619
+ *
1620
+ * Returns a {@link AgentCommandHandle} (re-used to keep the disposable shape uniform
1621
+ * across registries) whose `dispose()` removes this exact registration.
1622
+ */
1623
+ register<T = unknown>(reg: AgentDropRendererRegistration<T>): AgentCommandHandle;
1624
+ /**
1625
+ * Apps publish their live "draggable from focused window" set so the keyboard
1626
+ * "Attach from app…" menu can offer them. Hosts call this each time the user-visible
1627
+ * draggable list changes; passing an empty array clears the entry for this `appId`.
1628
+ */
1629
+ publishDraggables(appId: string, items: readonly AgentDraggableItem[]): void;
1630
+ /**
1631
+ * Reactive read of the draggable set published for `appId`. Empty when none. The
1632
+ * returned signal is stable across calls (cached by `appId`), so consumers can use
1633
+ * it as a stable input to `computed()`.
1634
+ */
1635
+ draggablesFor(appId: string): Signal<readonly AgentDraggableItem[]>;
1636
+ /** Lazy-init the per-appId writable bucket. Returns the writable handle for internal use. */
1637
+ private bucketFor;
1638
+ private _generation;
1639
+ private readonly _owners;
1640
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<AgentDropRegistry, never>;
1641
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<AgentDropRegistry>;
1642
+ }
1643
+
1644
+ /**
1645
+ * Single canonical way to declare a drag source for the agent input.
1646
+ *
1647
+ * Apply on any element a remote wants to make draggable into the `<fly-agent-input>`:
1648
+ *
1649
+ * ```html
1650
+ * <article [flyAgentDraggable]="() => buildPayload(trend)" flyAgentDraggableGhostId="trend-ghost">…</article>
1651
+ * ```
1652
+ *
1653
+ * On `dragstart` the directive:
1654
+ * 1. Resolves the payload (calls the builder fn if supplied; otherwise reads the
1655
+ * static value).
1656
+ * 2. Validates against {@link DEFAULT_AGENT_PAYLOAD_LIMITS} via
1657
+ * {@link validateAgentPayload}. On failure it `console.warn`s with the structured
1658
+ * reason and writes ONLY the `text/plain` fallback — the receiver still sees a
1659
+ * legible drop, but the typed envelope is skipped so a misconfigured app can't
1660
+ * corrupt downstream consumers with an oversize payload.
1661
+ * 3. Writes the typed MIME (`application/x-fly-agent-payload+json`) plus the
1662
+ * `text/plain` fallback so non-Fly drop targets still get useful text.
1663
+ * 4. Sets `effectAllowed = 'copy'` (we never *move* domain objects across windows).
1664
+ * 5. When `flyAgentDraggableGhostId` resolves to an element in the DOM, calls
1665
+ * `setDragImage` to use that element as the drag preview; otherwise the browser
1666
+ * default applies.
1667
+ *
1668
+ * Notes:
1669
+ * - Builder form is preferred over static — it captures fresh state at `dragstart`,
1670
+ * not at directive init. A static `AgentDragPayload` is fine for items whose
1671
+ * domain object never changes after the row mounts.
1672
+ * - The directive sets `[attr.draggable]="true"` so callers don't have to.
1673
+ */
1674
+ declare class FlyAgentDraggableDirective<T = unknown> {
1675
+ /**
1676
+ * Either a static payload or a builder evaluated at `dragstart` (preferred — captures
1677
+ * fresh state). `input.required` so misuse fails at template compile time.
1678
+ */
1679
+ readonly flyAgentDraggable: _angular_core.InputSignal<AgentDragPayload<T> | (() => AgentDragPayload<T>)>;
1680
+ /**
1681
+ * Optional ghost element id (an existing element in the DOM whose snapshot becomes
1682
+ * the drag image). Falls back to the default browser drag image when absent or the
1683
+ * element can't be resolved.
1684
+ *
1685
+ * Must be a valid HTML id (ASCII, letter-led). `getElementById` is intentional —
1686
+ * do NOT swap to `querySelector` without re-introducing this whitelist.
1687
+ */
1688
+ readonly flyAgentDraggableGhostId: _angular_core.InputSignal<string | undefined>;
1689
+ private readonly doc;
1690
+ onDragStart(ev: DragEvent): void;
1691
+ private resolvePayload;
1692
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<FlyAgentDraggableDirective<any>, never>;
1693
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FlyAgentDraggableDirective<any>, "[flyAgentDraggable]", never, { "flyAgentDraggable": { "alias": "flyAgentDraggable"; "required": true; "isSignal": true; }; "flyAgentDraggableGhostId": { "alias": "flyAgentDraggableGhostId"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1694
+ }
1695
+
1696
+ /**
1697
+ * Pure utility helpers for agent drag payload size guarding.
1698
+ *
1699
+ * Used by `flyAgentDraggable` at `dragstart` (Phase 2) and by app pre-flight code that
1700
+ * wants a clean payload before constructing the directive input. No Angular DI, no
1701
+ * runtime side effects — safe to import from anywhere.
1702
+ *
1703
+ * Performance notes:
1704
+ * - The `TextEncoder` is lazily allocated and cached. The constructor runs once per
1705
+ * module instance even under federation (singleton DS).
1706
+ * - String trimming uses `Intl.Segmenter` when available (all evergreen browsers
1707
+ * including Safari 14.1+) so we cut on grapheme boundaries — emoji ZWJ sequences,
1708
+ * combining marks, regional indicator pairs all stay intact. Fallback is
1709
+ * UTF-16 code-unit slice, which is a graceful degradation: it can split a
1710
+ * surrogate pair, but the resulting JSON is still syntactically valid and
1711
+ * downstream renderers handle the replacement char.
1712
+ */
1713
+ /**
1714
+ * Thrown by {@link trimAgentPayload} when even a fully string-trimmed envelope still
1715
+ * exceeds `maxJsonBytes` (e.g. an envelope with thousands of nested empty objects /
1716
+ * array entries / property keys). Subclasses `RangeError` so callers using the standard
1717
+ * `instanceof RangeError` check still match, while exposing the structured fields
1718
+ * (`actualBytes`, `limitBytes`, `reason: 'json_too_large'`) needed for telemetry and
1719
+ * symmetric handling with {@link AgentPayloadValidationResult} failures.
1720
+ */
1721
+ declare class AgentPayloadOversizeError extends RangeError {
1722
+ readonly actualBytes: number;
1723
+ readonly limitBytes: number;
1724
+ readonly reason: "json_too_large";
1725
+ constructor(message: string, actualBytes: number, limitBytes: number);
1726
+ }
1727
+ /** UTF-8 byte length of `s`. Cheap; cached encoder. */
1728
+ declare function utf8ByteLength(s: string): number;
1729
+ /**
1730
+ * Trim `input` so the UTF-8 byte length of the result is `<= maxBytes`. When trimming
1731
+ * occurs, append `ellipsis` (default `…`, U+2026) — its bytes are included in the cap.
1732
+ *
1733
+ * Idempotent: `trimAgentString(trimAgentString(s, n), n)` equals `trimAgentString(s, n)`.
1734
+ *
1735
+ * Edge cases:
1736
+ * - `maxBytes` shorter than the ellipsis itself returns an empty string.
1737
+ * - `Intl.Segmenter` (when present) ensures we never split a grapheme cluster.
1738
+ * - When the encoded length is already within the cap, returns `input` unchanged.
1739
+ */
1740
+ declare function trimAgentString(input: string, maxBytes: number, ellipsis?: string): string;
1741
+ /**
1742
+ * Validate an {@link AgentDragPayload} against the supplied limits (or
1743
+ * {@link DEFAULT_AGENT_PAYLOAD_LIMITS} when omitted). Returns `{ ok: true }` or a
1744
+ * structured failure with the offending field path and byte counts.
1745
+ *
1746
+ * Order of checks (cheap → expensive):
1747
+ * 1. `version` matches the frozen {@link AGENT_PAYLOAD_VERSION}.
1748
+ * 2. `kind` is a non-empty string.
1749
+ * 3. `plainTextFallback` fits its cap.
1750
+ * 4. Every string in `payload` (recursively) fits the per-field cap.
1751
+ * 5. The full `JSON.stringify(envelope)` fits the JSON cap.
1752
+ *
1753
+ * The walker stops on the first oversize string field — the intent is to surface
1754
+ * actionable feedback, not enumerate every offender.
1755
+ */
1756
+ declare function validateAgentPayload(payload: AgentDragPayload<unknown>, limits?: Partial<AgentPayloadLimits>): AgentPayloadValidationResult;
1757
+ /**
1758
+ * Returns a NEW payload with all string fields trimmed to fit the per-field cap, the
1759
+ * `plainTextFallback` trimmed to its cap, and the full envelope re-checked against the
1760
+ * JSON cap. Idempotent — a second call against the trimmed result yields equal output.
1761
+ *
1762
+ * Throws {@link AgentPayloadOversizeError} only if even an empty trim couldn't shrink it
1763
+ * under the cap (e.g. an envelope with 1000+ nested empty objects). The error subclasses
1764
+ * `RangeError`, so legacy `instanceof RangeError` checks still match. Apps that hit this
1765
+ * should re-shape `payload` rather than call again.
1766
+ */
1767
+ declare function trimAgentPayload<T>(payload: AgentDragPayload<T>, limits?: Partial<AgentPayloadLimits>): AgentDragPayload<T>;
1768
+
1769
+ /**
1770
+ * Cross-cutting locale catalog — used by the dictation language picker today, and
1771
+ * reusable for any future feature that needs a "pick a recognition / translation /
1772
+ * synthesis language" UI (independent of the shell UI locale).
1773
+ *
1774
+ * Keep this shape aligned with `Fly.Shared.Core`'s `LanguageDTO` so backend lookups
1775
+ * stay one-to-one. Translations of feature-specific strings (`detected`, `searching`,
1776
+ * `elevating`, `translating`, `generatingAnswersForYou`, `learnMore`,
1777
+ * `waitingForResponseMsg`) are carried verbatim from the canonical list — they are NOT
1778
+ * used by the agent input today but are preserved so we don't fork the data when the
1779
+ * translation feature ships.
1780
+ *
1781
+ * Kept in a separate module from {@link FLY_LOCALE_CATALOG} so consumers can
1782
+ * `import type { FlyLocaleEntry } from '@mohamedatia/fly-design-system'` without
1783
+ * pulling in the 31-row data table (tree-shaking when only the type is needed).
1784
+ */
1785
+ interface FlyLocaleEntry {
1786
+ /** Two-letter UPPERCASE prefix, e.g. `AR`. Display-friendly badge text. */
1787
+ readonly prefix: string;
1788
+ /** BCP-47 / dialect tag, e.g. `ar-AE`. Use this for `SpeechRecognition.lang`. */
1789
+ readonly dialect: string;
1790
+ readonly englishName: string;
1791
+ readonly arabicName: string;
1792
+ readonly nativeName: string;
1793
+ readonly detected: string;
1794
+ readonly searching: string;
1795
+ readonly elevating: string;
1796
+ readonly translating: string;
1797
+ readonly generatingAnswersForYou: string;
1798
+ readonly learnMore: string;
1799
+ readonly waitingForResponseMsg: string;
1800
+ /** True for `he-IL`, `ur-IN`, `ar-AE`. Used to set `dir` on the dictation chip / interim text. */
1801
+ readonly isRtl: boolean;
1802
+ }
1803
+
1804
+ /**
1805
+ * Verbatim TypeScript port of the C# `LanguageDTO[] _AllLanguages` array supplied by
1806
+ * product. **31 entries**, in the canonical order. Three RTL flagged: `he-IL`, `ur-IN`,
1807
+ * `ar-AE`. Includes `cmn-Hant-TW` for Chinese and `ka-GE` for Georgian.
1808
+ *
1809
+ * The order matches the C# source byte-for-byte — do not re-sort. Downstream tests
1810
+ * (`fly-locale-catalog.spec.ts`) pin the count, the unique-key constraints, and the
1811
+ * RTL set; if you reorder or add an entry, those tests will guide the fix.
1812
+ *
1813
+ * The strings carry Cyrillic, Hebrew, Vietnamese, Thai, Indonesian, Bengali, Devanagari,
1814
+ * Urdu, Catalan, Polish, Korean, Russian, Japanese, Han, Turkish, Romanian, Norwegian,
1815
+ * Hungarian, Greek, Finnish, Danish, Dutch, Swedish, Portuguese, Italian, German,
1816
+ * Spanish, French, English, Arabic, and Georgian. The file is saved as UTF-8 without
1817
+ * BOM (matches every other locale file in the repo). The `…` (U+2026) ellipsis is
1818
+ * preserved verbatim.
1819
+ */
1820
+ declare const FLY_LOCALE_CATALOG: readonly FlyLocaleEntry[];
1821
+ /**
1822
+ * Lookup by BCP-47 dialect (e.g. `'ar-AE'`). Case-insensitive per BCP-47 §2.1.1
1823
+ * (subtags are case-insensitive on the wire) — accepts `'en-US'`, `'en-us'`,
1824
+ * `'EN-US'`, etc. all as the canonical EN entry. The catalog itself keeps its
1825
+ * canonical casing for display.
1826
+ *
1827
+ * Returns `undefined` when not found; callers decide whether to fall back to `'en-US'`.
1828
+ */
1829
+ declare function findLocaleByDialect(dialect: string): FlyLocaleEntry | undefined;
1830
+ /**
1831
+ * Lookup by short prefix (e.g. `'AR'`). Prefixes are unique in the catalog (asserted by tests).
1832
+ * Case-insensitive — accepts `'AR'`, `'ar'`, `'Ar'`. Catalog stores prefixes uppercase
1833
+ * for display.
1834
+ */
1835
+ declare function findLocaleByPrefix(prefix: string): FlyLocaleEntry | undefined;
1836
+ /**
1837
+ * Type-narrowing helper for entries flagged RTL.
1838
+ *
1839
+ * Named with the `Entry` suffix to avoid colliding with the existing
1840
+ * `isRtlLocale(lang: string)` from `i18n.service.ts`, which answers a different
1841
+ * question (is the *UI* locale code RTL?). Both helpers coexist in `public-api`.
1842
+ */
1843
+ declare function isRtlLocaleEntry(entry: FlyLocaleEntry): entry is FlyLocaleEntry & {
1844
+ isRtl: true;
1845
+ };
1846
+
1224
1847
  /**
1225
1848
  * Stable error codes returned by audience-aware backend endpoints. Mirror of
1226
1849
  * `Fly.Shared.Core.Audience.AudienceErrorCodes`. Compare on these strings, not on HTTP
@@ -1234,6 +1857,6 @@ declare const AUDIENCE_ERROR_CODES: {
1234
1857
  };
1235
1858
  type AudienceErrorCode = (typeof AUDIENCE_ERROR_CODES)[keyof typeof AUDIENCE_ERROR_CODES];
1236
1859
 
1237
- export { AUDIENCE_ERROR_CODES, AUDIENCE_LIMITS, AUDIENCE_PRESETS, AUDIENCE_TERM_KINDS, AudienceBuilderComponent, AuthService, ContextMenuComponent, DEFAULT_FLY_THEME_MODE, DialogResult, FLY_THEME_MODE_IDS, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyThemeService, 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, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale, normalizeFlyTheme };
1238
- export type { AppEveryonePrincipal, AppEveryoneTerm, AudienceEditTarget, AudienceErrorCode, AudienceFilter, AudienceOptions, AudiencePresetKind, AudienceTerm, AudienceTermKind, ChartTerm, ChildWindowData, ContextMenuItem, ContextMenuSection, DesktopApp, DesktopAppKind, FlyFileInfo, FlyThemeMode, LaunchContext, LoadBundleOptions, MessageBoxButton, MessageBoxOptions, MockAuthConfig, OpenWindowOptions, OuPrincipal, OuTerm, PresetTerm, RemoteAppDef, RoleOuLookupRow, RolePrincipal, RolesTerm, ShareOrgChartOption, ShareOuNode, SharePanelLevelOption, SharePermissionEntry, SharePrincipal, SharePrincipalKind, ShareUserResult, User, UserPrincipal, UsersTerm, WindowInstance, WindowState };
1860
+ export { AGENT_DRAG_MIME, AGENT_PAYLOAD_VERSION, 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, FLY_LOCALE_CATALOG, FLY_THEME_MODE_IDS, FlyAgentDraggableDirective, FlyBlockUiComponent, FlyFileUploadComponent, FlyImageUploadComponent, FlyRemoteRouter, FlyThemeService, 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, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, findLocaleByDialect, findLocaleByPrefix, isRtlLocale, isRtlLocaleEntry, normalizeFlyTheme, trimAgentPayload, trimAgentString, utf8ByteLength, validateAgentPayload };
1861
+ export type { AgentChipHostInputs, AgentCommand, AgentCommandHandle, AgentCommandRegistration, AgentCommandScope, AgentDragPayload, AgentDraggableItem, AgentDropChipMode, AgentDropRendererRegistration, AgentPayloadLimits, AgentPayloadValidationResult, AppEveryonePrincipal, AppEveryoneTerm, AudienceEditTarget, AudienceErrorCode, AudienceFilter, AudienceOptions, AudiencePresetKind, AudienceTerm, AudienceTermKind, ChartTerm, ChildWindowData, ContextMenuItem, ContextMenuSection, DesktopApp, DesktopAppKind, DialogResultWithAcknowledgement, FlyFileInfo, FlyLocaleEntry, FlyThemeMode, LaunchContext, LoadBundleOptions, MessageBoxButton, MessageBoxDontAskAgainConfig, MessageBoxOptions, MessageBoxOptionsWithAcknowledgement, MockAuthConfig, OpenWindowOptions, OuPrincipal, OuTerm, PresetTerm, RemoteAppDef, RoleOuLookupRow, RolePrincipal, RolesTerm, ShareOrgChartOption, ShareOuNode, SharePanelLevelOption, SharePermissionEntry, SharePrincipal, SharePrincipalKind, ShareUserResult, User, UserPrincipal, UsersTerm, WindowInstance, WindowState };
1239
1862
  //# sourceMappingURL=mohamedatia-fly-design-system.d.ts.map