@gntik-ai/blocks 0.7.0 → 0.8.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/dist/index.d.ts CHANGED
@@ -1815,6 +1815,13 @@ export declare function CreateApiKeyDialog({ scopes, expiries, defaultExpiry, on
1815
1815
  //#region src/forms/CreateApiKeyDialog/block.meta.d.ts
1816
1816
  declare const meta$13: BlockMeta;
1817
1817
  //#endregion
1818
+ //#region src/forms/CredentialDisclosureDialog/types.d.ts
1819
+ interface CredentialSecret {
1820
+ id: string;
1821
+ label: string;
1822
+ value: string;
1823
+ }
1824
+ //#endregion
1818
1825
  //#region src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.d.ts
1819
1826
  export interface CredentialDisclosure {
1820
1827
  variant: 'stored' | 'fresh';
@@ -1824,9 +1831,21 @@ export interface CredentialDisclosure {
1824
1831
  secret: string;
1825
1832
  }
1826
1833
  export interface CredentialDisclosureDialogProps {
1834
+ /** Additional masked values; ids must be unique, non-empty and not "secret". */
1835
+ additionalSecrets?: readonly CredentialSecret[];
1836
+ /** Mask a fresh primary secret; read at the start of each session. */
1837
+ maskFresh?: boolean;
1838
+ /** Redacted content below the secrets; clear secret-bearing content on close. */
1839
+ revealContent?: ReactNode;
1840
+ /** Presentation fixed for each non-null opening. Defaults to a modal dialog. */
1841
+ presentation?: 'dialog' | 'inline';
1842
+ /** Called after copying with the secret id (primary: "secret"), never its value. */
1843
+ onCopy?: (id: string) => void;
1844
+ /** Label-only success announcement override, used only in multi-secret mode. */
1845
+ copiedAnnouncementFor?: (label: string) => string;
1827
1846
  /** Set only after a successful response. Leave null while pending or on failure. */
1828
1847
  disclosure: CredentialDisclosure | null;
1829
- /** Clear disclosure to null on dismissal, dropping the secret in the consumer. */
1848
+ /** Clear disclosure, additionalSecrets and secret-bearing revealContent on dismissal. */
1830
1849
  onClose: () => void;
1831
1850
  /** Resolve the current row action at close time, including after list reloads. */
1832
1851
  resolveReturnFocus?: () => HTMLElement | null;
@@ -1848,9 +1867,10 @@ export interface CredentialDisclosureDialogProps {
1848
1867
  closeLabel?: string;
1849
1868
  }
1850
1869
  /**
1851
- * Discloses an existing credential only after a successful response. Consumers keep disclosure
1852
- * null while pending or on failure, and clear it on close. No pending/error state or internal
1853
- * secret retention. A fresh secret requires a new successful response to be shown again.
1870
+ * Render only successful responses. Keep disclosure null while pending or on failure.
1871
+ * On close, clear disclosure, additionalSecrets and any secret-bearing revealContent.
1872
+ * No pending/error state or internal secret retention. Retained dismissed sessions stay closed;
1873
+ * a changed credential/value or a null-to-value opening starts a new session.
1854
1874
  */
1855
1875
  export declare function CredentialDisclosureDialog(props: CredentialDisclosureDialogProps): import("react").JSX.Element | null;
1856
1876
  //#endregion
@@ -3730,5 +3750,5 @@ export declare function SessionsDevices({ sessions, onRevoke, onRevokeOthers, ti
3730
3750
  //#region src/team/SessionsDevices/block.meta.d.ts
3731
3751
  declare const meta$60: BlockMeta;
3732
3752
  //#endregion
3733
- export { type ApiKeyExpiry, type ApiKeyScope, type Assistant, type AssistantStatus, type AuthFormAccessibilityProps, type AuthHeaderProps, type AuthIdentifierConfig, type AuthSignUpField, type AuthSignUpValidationMessages, type AuthUnavailableState, type AuthValidationMessages, type BlockMeta, type CodeFile, type CodePanelMarker, type CodeProblem, type ColumnResizeHandleProps, type ColumnVisibilityMenuProps, type ColumnWidths, type ConfirmDestructiveDialogProps, type ConsoleEntry, type CostPoint, type DangerZoneAction, type DataTableCellEdit, type DataTableCellEditHandler, type DataTableCellEditResult, type DataTableLabels, type DataTableProps, type DataTableView, type DataTableViewState, ViewSwitcher as DataTableViewSwitcher, type ViewSwitcherProps as DataTableViewSwitcherProps, type DensityToggleProps, type DeploymentRow, type DeviceKind, type DeviceSession, type EditableMember, type EvalMetric, type EvalRow, type EventKind, type EventTone, type FeedbackAction, type FlowConnection, type FlowConnectionRejection, type FlowEdge, type FlowHandle, type FlowNode, type FlowNodeData, type FlowNodeKind, type FlowNodeRenderProps, type FlowNodeRenderer, type FlowTone, type FlowValidationIssue, type Invitation, type LogLevel, type LogLine, type MarketingAction, type Member, type ModelKey, type ModelKeyStatus, type MonacoReady, type NodeInspectorProps, type NodePaletteProps, type NotificationChannel, type NotificationEvent, type NotificationMatrixValue, type PageChromeAction, type PaletteItem, type PanelHeading, type PinSide, type PinnedColumns, type PlanSummaryProps, type ProblemSeverity, type Profile, type ProfileMetaItem, type RoleOption, type RunConsoleProps, type ScheduleEvent, type SpanDetailsProps, type SpanKind, type SsoProvider, type SummaryPlan, type TokenUsagePeriod, type TraceSpan, type TrafficRow, type UploadItem, type UploadStatus, type ValidationIssue, meta$1 as activityFeedMeta, meta$2 as agendaListMeta, meta$3 as appSidebarMeta, meta$4 as appTopbarMeta, meta$5 as assistantCardGridMeta, meta$6 as bulkActionBarMeta, meta$7 as chartCardMeta, meta$8 as codePanelMeta, meta$9 as commentThreadMeta, meta$10 as comparisonTableMeta, meta$11 as confirmDestructiveMeta, meta$12 as costBreakdownMeta, meta$13 as createApiKeyDialogMeta, meta$14 as credentialDisclosureDialogMeta, meta$15 as cursorPagerFooterMeta, meta$16 as dangerZoneMeta, meta$17 as dataTableMeta, meta$18 as dateRangeFilterMeta, meta$19 as descriptionListCardMeta, meta$20 as dialogWizardMeta, meta$21 as emptyStatesMeta, meta$22 as environmentBadgeMeta, meta$23 as errorPanelMeta, meta$24 as evaluationScorecardMeta, meta$25 as faqMeta, meta$26 as featureGridMeta, meta$27 as fileUploadPanelMeta, meta$28 as filterBarMeta, meta$29 as flowBuilderMeta, meta$30 as forgotPasswordFormMeta, meta$31 as formSectionMeta, meta$32 as globalSearchMeta, meta$33 as heroMeta, meta$34 as inlineCalloutMeta, meta$35 as inlineEditRowMeta, meta$36 as inviteMembersDialogMeta, meta$37 as invoiceTableMeta, meta$38 as kpiRowMeta, meta$39 as loadMoreFooterMeta, meta$40 as logViewerMeta, meta$41 as membersTableMeta, meta$42 as mfaChallengeMeta, meta$43 as modelKeysListMeta, meta$44 as monthCalendarMeta, meta$45 as notificationMatrixMeta, meta$46 as pageHeaderMeta, meta$47 as pageToolbarMeta, meta$48 as paginationFooterMeta, meta$49 as paymentMethodCardMeta, meta$50 as pendingInvitationsMeta, meta$51 as planCardMeta, meta$52 as planDefinitionCardMeta, meta$53 as pricingTableMeta, meta$54 as profileHeaderMeta, meta$55 as promptEditorMeta, meta$56 as quotaMetersMeta, meta$57 as resetPasswordFormMeta, meta$58 as resourceCardGridMeta, deviceSessions as sampleDeviceSessions, invitations$1 as sampleInvitations, members$1 as sampleMembers, profile$1 as sampleProfile, sampleEvents as sampleScheduleEvents, sampleToday as sampleScheduleToday, meta$59 as sectionHeaderMeta, meta$60 as sessionsDevicesMeta, meta$61 as settingsRowMeta, meta$62 as signInFormMeta, meta$63 as signUpFormMeta, meta$64 as skeletonsMeta, meta$65 as spendVsBudgetMeta, meta$66 as ssoButtonsMeta, meta$67 as statCardMeta, meta$68 as statusTimelineMeta, meta$69 as stickyActionBarMeta, meta$70 as subordinateListMeta, meta$71 as systemBannerMeta, meta$72 as timeSlotPickerMeta, meta$73 as tokenCostCardMeta, meta$74 as traceWaterfallMeta, meta$75 as truncatedCollectionFooterMeta, validationIssues$1 as validationIssues, meta$76 as validationSummaryMeta, meta$77 as weekCalendarMeta };
3753
+ export { type ApiKeyExpiry, type ApiKeyScope, type Assistant, type AssistantStatus, type AuthFormAccessibilityProps, type AuthHeaderProps, type AuthIdentifierConfig, type AuthSignUpField, type AuthSignUpValidationMessages, type AuthUnavailableState, type AuthValidationMessages, type BlockMeta, type CodeFile, type CodePanelMarker, type CodeProblem, type ColumnResizeHandleProps, type ColumnVisibilityMenuProps, type ColumnWidths, type ConfirmDestructiveDialogProps, type ConsoleEntry, type CostPoint, type CredentialSecret, type DangerZoneAction, type DataTableCellEdit, type DataTableCellEditHandler, type DataTableCellEditResult, type DataTableLabels, type DataTableProps, type DataTableView, type DataTableViewState, ViewSwitcher as DataTableViewSwitcher, type ViewSwitcherProps as DataTableViewSwitcherProps, type DensityToggleProps, type DeploymentRow, type DeviceKind, type DeviceSession, type EditableMember, type EvalMetric, type EvalRow, type EventKind, type EventTone, type FeedbackAction, type FlowConnection, type FlowConnectionRejection, type FlowEdge, type FlowHandle, type FlowNode, type FlowNodeData, type FlowNodeKind, type FlowNodeRenderProps, type FlowNodeRenderer, type FlowTone, type FlowValidationIssue, type Invitation, type LogLevel, type LogLine, type MarketingAction, type Member, type ModelKey, type ModelKeyStatus, type MonacoReady, type NodeInspectorProps, type NodePaletteProps, type NotificationChannel, type NotificationEvent, type NotificationMatrixValue, type PageChromeAction, type PaletteItem, type PanelHeading, type PinSide, type PinnedColumns, type PlanSummaryProps, type ProblemSeverity, type Profile, type ProfileMetaItem, type RoleOption, type RunConsoleProps, type ScheduleEvent, type SpanDetailsProps, type SpanKind, type SsoProvider, type SummaryPlan, type TokenUsagePeriod, type TraceSpan, type TrafficRow, type UploadItem, type UploadStatus, type ValidationIssue, meta$1 as activityFeedMeta, meta$2 as agendaListMeta, meta$3 as appSidebarMeta, meta$4 as appTopbarMeta, meta$5 as assistantCardGridMeta, meta$6 as bulkActionBarMeta, meta$7 as chartCardMeta, meta$8 as codePanelMeta, meta$9 as commentThreadMeta, meta$10 as comparisonTableMeta, meta$11 as confirmDestructiveMeta, meta$12 as costBreakdownMeta, meta$13 as createApiKeyDialogMeta, meta$14 as credentialDisclosureDialogMeta, meta$15 as cursorPagerFooterMeta, meta$16 as dangerZoneMeta, meta$17 as dataTableMeta, meta$18 as dateRangeFilterMeta, meta$19 as descriptionListCardMeta, meta$20 as dialogWizardMeta, meta$21 as emptyStatesMeta, meta$22 as environmentBadgeMeta, meta$23 as errorPanelMeta, meta$24 as evaluationScorecardMeta, meta$25 as faqMeta, meta$26 as featureGridMeta, meta$27 as fileUploadPanelMeta, meta$28 as filterBarMeta, meta$29 as flowBuilderMeta, meta$30 as forgotPasswordFormMeta, meta$31 as formSectionMeta, meta$32 as globalSearchMeta, meta$33 as heroMeta, meta$34 as inlineCalloutMeta, meta$35 as inlineEditRowMeta, meta$36 as inviteMembersDialogMeta, meta$37 as invoiceTableMeta, meta$38 as kpiRowMeta, meta$39 as loadMoreFooterMeta, meta$40 as logViewerMeta, meta$41 as membersTableMeta, meta$42 as mfaChallengeMeta, meta$43 as modelKeysListMeta, meta$44 as monthCalendarMeta, meta$45 as notificationMatrixMeta, meta$46 as pageHeaderMeta, meta$47 as pageToolbarMeta, meta$48 as paginationFooterMeta, meta$49 as paymentMethodCardMeta, meta$50 as pendingInvitationsMeta, meta$51 as planCardMeta, meta$52 as planDefinitionCardMeta, meta$53 as pricingTableMeta, meta$54 as profileHeaderMeta, meta$55 as promptEditorMeta, meta$56 as quotaMetersMeta, meta$57 as resetPasswordFormMeta, meta$58 as resourceCardGridMeta, deviceSessions as sampleDeviceSessions, invitations$1 as sampleInvitations, members$1 as sampleMembers, profile$1 as sampleProfile, sampleEvents as sampleScheduleEvents, sampleToday as sampleScheduleToday, meta$59 as sectionHeaderMeta, meta$60 as sessionsDevicesMeta, meta$61 as settingsRowMeta, meta$62 as signInFormMeta, meta$63 as signUpFormMeta, meta$64 as skeletonsMeta, meta$65 as spendVsBudgetMeta, meta$66 as ssoButtonsMeta, meta$67 as statCardMeta, meta$68 as statusTimelineMeta, meta$69 as stickyActionBarMeta, meta$70 as subordinateListMeta, meta$71 as systemBannerMeta, meta$72 as timeSlotPickerMeta, meta$73 as tokenCostCardMeta, meta$74 as traceWaterfallMeta, meta$75 as truncatedCollectionFooterMeta, validationIssues$1 as validationIssues, meta$76 as validationSummaryMeta, meta$77 as weekCalendarMeta };
3734
3754
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { Fragment, createElement, useCallback, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
+ import { Fragment, createElement, useCallback, useEffect, useEffectEvent, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
3
3
  import { Activity, Archive, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, BarChart3, Bookmark, Bot, Boxes, Braces, Building2, CalendarCheck, CalendarDays, Camera, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, CircleAlert, CircleCheck, CircleHelp, CircleX, Clock, Cog, Columns3, Copy, CreditCard, Database, Download, ExternalLink, Eye, FileCode, FileText, FolderKanban, FolderPlus, Gauge, GitBranch, Globe, HardDrive, Home, Inbox, Info, KeyRound, Layers, LayoutGrid, List, ListFilter, Lock, LogOut, Mail, MailCheck, MapPin, Minus, Monitor, MousePointerClick, Pencil, Play, PlugZap, Plus, RefreshCw, Rocket, RotateCcw, RotateCw, Rows2, Rows3, Search, SearchX, Server, ServerCrash, Settings, ShieldCheck, SlidersHorizontal, Smartphone, Sparkles, Table2, Tablet, Trash2, TriangleAlert, Upload, User, UserMinus, UserPlus, Users, Workflow, Wrench, X, Zap } from "@gntik-ai/icons";
4
4
  import { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, Alert, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Avatar, Badge, Breadcrumbs, Button, Card, CardAction, CardBody, CardDescription, CardHeader, CardTitle, Checkbox, CheckboxGroup, ClickableCard, CodeBlock, Collapsible, CollapsiblePanel, CollapsibleTrigger, CommandPalette, CommandPaletteTrigger, CopyButton, CursorPagination, DEFAULT_DATE_RANGE_PRESETS, DateRangePicker, DensityProvider, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, EmptyState, Field, FieldDescription, FieldError, FieldLabel, Fieldset, FieldsetLegend, IconButton, Input, Link, LiveAnnouncer, Logo, Menu, MenuCheckboxItem, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Meter, MoreMenu, NavList, NotificationsPopover, OtpInput, Pagination, PasswordInput, Progress, Radio, RadioGroup, ResizablePanel, ResizablePanelGroup, ResizeHandle, SecretField, Select, SelectContent, SelectItem, SelectTrigger, SimpleSelect, Skeleton, Slider, Sparkline, Spinner, StatusDot, StatusTag, Switch, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsList, TabsPanel, TabsTab, Textarea, ThemeCycleButton, ThemeSwitcher, Timestamp, Toggle, ToggleGroup, Token, TokenInput, Toolbar, ToolbarButton, ToolbarEnd, ToolbarInput, ToolbarSeparator, ToolbarStart, UserMenu, WizardLayout, WorkspaceSwitcher, buttonVariants, cn, nextSortDirection, tableVariants, toDate, useAnnounce, useDensity, useHasLiveAnnouncer, useI18n } from "@gntik-ai/ui";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -8056,11 +8056,78 @@ const meta$12 = {
8056
8056
  ]
8057
8057
  };
8058
8058
  //#endregion
8059
+ //#region src/forms/CredentialDisclosureDialog/SecretCopy.tsx
8060
+ /** Each secret owns feedback so simultaneous settlements cannot overwrite each other. */
8061
+ function SecretCopy({ secret, options, grouped = false, masked = true, warning }) {
8062
+ const { t } = useI18n();
8063
+ const [pending, setPending] = useState(false);
8064
+ const copied = options.copiedLabel ?? t("credentialDisclosure.copied");
8065
+ const content = /* @__PURE__ */ jsxs(LiveAnnouncer, { children: [
8066
+ warning,
8067
+ /* @__PURE__ */ jsx("div", {
8068
+ "data-kit-redact": true,
8069
+ children: masked ? /* @__PURE__ */ jsx(SecretField, {
8070
+ value: secret.value,
8071
+ copyable: false,
8072
+ "aria-label": secret.label,
8073
+ showLabel: options.showLabel ?? t("credentialDisclosure.show"),
8074
+ hideLabel: options.hideLabel ?? t("credentialDisclosure.hide")
8075
+ }) : /* @__PURE__ */ jsx(CodeBlock, {
8076
+ code: secret.value,
8077
+ copyable: false,
8078
+ label: secret.label,
8079
+ wrapToggle: false,
8080
+ defaultWrap: true
8081
+ })
8082
+ }),
8083
+ /* @__PURE__ */ jsx(CopyButton, {
8084
+ value: () => {
8085
+ if (grouped) setPending(true);
8086
+ return secret.value;
8087
+ },
8088
+ loading: grouped && pending,
8089
+ display: "label",
8090
+ label: options.copyLabel ?? t("credentialDisclosure.copy"),
8091
+ copiedLabel: copied,
8092
+ announcement: grouped ? options.copiedAnnouncementFor?.(secret.label) ?? `${secret.label}: ${copied}` : options.successAnnouncement ?? t("credentialDisclosure.success"),
8093
+ failureLabel: options.failureAnnouncement ?? t("credentialDisclosure.failure"),
8094
+ failureAnnouncement: options.failureAnnouncement ?? t("credentialDisclosure.failure"),
8095
+ failurePoliteness: "polite",
8096
+ onCopy: () => {
8097
+ if (grouped) setPending(false);
8098
+ options.onCopy?.(secret.id);
8099
+ },
8100
+ onCopyError: () => {
8101
+ if (grouped) setPending(false);
8102
+ }
8103
+ })
8104
+ ] });
8105
+ return grouped ? /* @__PURE__ */ jsx("div", {
8106
+ role: "group",
8107
+ "aria-label": secret.label,
8108
+ children: content
8109
+ }) : content;
8110
+ }
8111
+ //#endregion
8112
+ //#region src/forms/CredentialDisclosureDialog/focus.ts
8113
+ /** Programmatic focus permits negative tabIndex, but never hidden, inert or disabled targets. */
8114
+ function usableFocusTarget(element) {
8115
+ if (!(element instanceof HTMLElement) || !element.isConnected || element === document.body) return false;
8116
+ if (element.matches(":disabled, [disabled], [aria-disabled=\"true\"], input[type=\"hidden\"]")) return false;
8117
+ for (let ancestor = element; ancestor; ancestor = ancestor.parentElement) {
8118
+ if (ancestor.hidden || ancestor.hasAttribute("inert") || ancestor.getAttribute("aria-disabled") === "true") return false;
8119
+ const style = getComputedStyle(ancestor);
8120
+ if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse" || style.contentVisibility === "hidden") return false;
8121
+ }
8122
+ return element.tabIndex >= 0 || element.hasAttribute("tabindex") || element.isContentEditable;
8123
+ }
8124
+ //#endregion
8059
8125
  //#region src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.tsx
8060
8126
  /**
8061
- * Discloses an existing credential only after a successful response. Consumers keep disclosure
8062
- * null while pending or on failure, and clear it on close. No pending/error state or internal
8063
- * secret retention. A fresh secret requires a new successful response to be shown again.
8127
+ * Render only successful responses. Keep disclosure null while pending or on failure.
8128
+ * On close, clear disclosure, additionalSecrets and any secret-bearing revealContent.
8129
+ * No pending/error state or internal secret retention. Retained dismissed sessions stay closed;
8130
+ * a changed credential/value or a null-to-value opening starts a new session.
8064
8131
  */
8065
8132
  function CredentialDisclosureDialog(props) {
8066
8133
  if (!props.disclosure) return null;
@@ -8071,93 +8138,182 @@ function CredentialDisclosureDialog(props) {
8071
8138
  }
8072
8139
  function DisclosureOpening(props) {
8073
8140
  const [opener] = useState(() => typeof document === "undefined" ? null : document.activeElement);
8141
+ const [presentation] = useState(props.presentation ?? "dialog");
8142
+ const root = useRef(null);
8143
+ const target = useRef(null);
8144
+ const active = useRef(opener);
8145
+ const started = useRef(false);
8146
+ const activeWasInside = useRef(false);
8147
+ const returnFocus = () => {
8148
+ const current = props.resolveReturnFocus?.();
8149
+ if (usableFocusTarget(current)) return current;
8150
+ const fallback = props.fallbackFocus?.();
8151
+ if (usableFocusTarget(fallback)) return fallback;
8152
+ return usableFocusTarget(opener) ? opener : false;
8153
+ };
8154
+ const restore = useEffectEvent(() => {
8155
+ if (root.current?.contains(document.activeElement)) {
8156
+ const element = returnFocus();
8157
+ if (element) element.focus();
8158
+ }
8159
+ });
8160
+ useLayoutEffect(() => {
8161
+ const track = () => {
8162
+ if (document.activeElement !== document.body) {
8163
+ active.current = document.activeElement;
8164
+ activeWasInside.current = root.current?.contains(document.activeElement) ?? false;
8165
+ }
8166
+ };
8167
+ document.addEventListener("focusin", track);
8168
+ return () => {
8169
+ document.removeEventListener("focusin", track);
8170
+ restore();
8171
+ };
8172
+ }, [presentation]);
8173
+ useLayoutEffect(() => {
8174
+ if (activeWasInside.current && active.current && !active.current.isConnected) target.current?.focus();
8175
+ });
8176
+ const seen = /* @__PURE__ */ new Set(["secret", ""]);
8177
+ const additionalSecrets = (props.additionalSecrets ?? []).filter(({ id }) => {
8178
+ if (seen.has(id)) return false;
8179
+ seen.add(id);
8180
+ return true;
8181
+ });
8074
8182
  const { variant, credentialId, secret } = props.disclosure;
8075
8183
  return /* @__PURE__ */ jsx(DisclosureSession, {
8076
8184
  ...props,
8077
- opener
8185
+ presentation,
8186
+ additionalSecrets,
8187
+ onRootRef: (node) => {
8188
+ root.current = node;
8189
+ },
8190
+ onTargetRef: (node) => {
8191
+ target.current = node;
8192
+ },
8193
+ onPopupRef: (node) => {
8194
+ root.current = node;
8195
+ target.current = node;
8196
+ },
8197
+ containsFocus: () => root.current?.contains(document.activeElement) ?? false,
8198
+ initialFocus: () => {
8199
+ const shouldFocus = !started.current || activeWasInside.current && !active.current?.isConnected;
8200
+ started.current = true;
8201
+ return shouldFocus ? root.current : false;
8202
+ },
8203
+ returnFocus
8078
8204
  }, JSON.stringify([
8079
8205
  variant,
8080
8206
  credentialId,
8081
- secret
8207
+ secret,
8208
+ ...additionalSecrets.map(({ id, value }) => [id, value])
8082
8209
  ]));
8083
8210
  }
8084
- function DisclosureSession({ disclosure, onClose, resolveReturnFocus, fallbackFocus, opener, ...labels }) {
8211
+ function DisclosureSession(props) {
8085
8212
  const { t } = useI18n();
8213
+ const { disclosure, onRootRef, onTargetRef, onPopupRef, initialFocus, containsFocus, presentation, additionalSecrets = [] } = props;
8086
8214
  const [open, setOpen] = useState(true);
8087
- const popup = useRef(null);
8215
+ const [maskFresh] = useState(props.maskFresh ?? false);
8088
8216
  const dismissed = useRef(false);
8089
- const returnFocus = () => {
8090
- const target = resolveReturnFocus?.();
8091
- if (usableFocusTarget(target)) return target;
8092
- const fallback = fallbackFocus?.();
8093
- if (usableFocusTarget(fallback)) return fallback;
8094
- return usableFocusTarget(opener) ? opener : false;
8095
- };
8096
- const closeLabel = labels.closeLabel ?? t("credentialDisclosure.close");
8097
- const secretLabel = labels.secretLabel ?? t("credentialDisclosure.secret");
8217
+ const headingId = useId();
8218
+ const close = () => {
8219
+ if (dismissed.current) return;
8220
+ dismissed.current = true;
8221
+ if (presentation === "inline" && containsFocus()) {
8222
+ const element = props.returnFocus();
8223
+ if (element) element.focus();
8224
+ }
8225
+ setOpen(false);
8226
+ props.onClose();
8227
+ };
8228
+ const closeLabel = props.closeLabel ?? t("credentialDisclosure.close");
8229
+ const title = props.title ?? t("credentialDisclosure.title");
8230
+ const description = props.description ?? t("credentialDisclosure.description");
8231
+ const secretLabel = props.secretLabel ?? t("credentialDisclosure.secret");
8232
+ const warning = disclosure.variant === "fresh" ? /* @__PURE__ */ jsx(Alert, {
8233
+ role: presentation === "inline" ? "note" : "alert",
8234
+ tone: "warning",
8235
+ title: props.warningTitle ?? t("credentialDisclosure.warningTitle"),
8236
+ description: props.warningText ?? t("credentialDisclosure.warningText")
8237
+ }) : null;
8238
+ const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
8239
+ /* @__PURE__ */ jsxs("dl", {
8240
+ className: "grid grid-cols-2 gap-2 text-sm",
8241
+ children: [
8242
+ /* @__PURE__ */ jsx("dt", {
8243
+ className: "text-muted-foreground",
8244
+ children: props.credentialIdLabel ?? t("credentialDisclosure.credentialId")
8245
+ }),
8246
+ /* @__PURE__ */ jsx("dd", {
8247
+ className: "break-all font-mono",
8248
+ children: disclosure.credentialId
8249
+ }),
8250
+ /* @__PURE__ */ jsx("dt", {
8251
+ className: "text-muted-foreground",
8252
+ children: props.expiryLabel ?? t("credentialDisclosure.expiry")
8253
+ }),
8254
+ /* @__PURE__ */ jsx("dd", { children: disclosure.expiresAt })
8255
+ ]
8256
+ }),
8257
+ /* @__PURE__ */ jsx(SecretCopy, {
8258
+ secret: {
8259
+ id: "secret",
8260
+ label: secretLabel,
8261
+ value: disclosure.secret
8262
+ },
8263
+ options: props,
8264
+ grouped: additionalSecrets.length > 0,
8265
+ masked: disclosure.variant === "stored" || maskFresh,
8266
+ warning
8267
+ }),
8268
+ additionalSecrets.map((secret) => /* @__PURE__ */ jsx(SecretCopy, {
8269
+ secret,
8270
+ options: props,
8271
+ grouped: true
8272
+ }, secret.id)),
8273
+ props.revealContent != null && /* @__PURE__ */ jsx("div", {
8274
+ "data-kit-redact": true,
8275
+ children: props.revealContent
8276
+ })
8277
+ ] });
8278
+ if (presentation === "inline") {
8279
+ if (!open) return null;
8280
+ return /* @__PURE__ */ jsxs("section", {
8281
+ ref: onRootRef,
8282
+ "aria-labelledby": headingId,
8283
+ className: "grid gap-4",
8284
+ children: [
8285
+ /* @__PURE__ */ jsx("h2", {
8286
+ id: headingId,
8287
+ tabIndex: -1,
8288
+ ref: onTargetRef,
8289
+ children: title
8290
+ }),
8291
+ /* @__PURE__ */ jsx("p", { children: description }),
8292
+ body,
8293
+ /* @__PURE__ */ jsx(Button, {
8294
+ variant: "secondary",
8295
+ onClick: close,
8296
+ children: closeLabel
8297
+ })
8298
+ ]
8299
+ });
8300
+ }
8098
8301
  return /* @__PURE__ */ jsx(Dialog, {
8099
8302
  open,
8100
8303
  onOpenChange: (next) => {
8101
- if (!next && !dismissed.current) {
8102
- dismissed.current = true;
8103
- setOpen(false);
8104
- onClose();
8105
- }
8304
+ if (!next) close();
8106
8305
  },
8107
8306
  children: /* @__PURE__ */ jsx(DialogContent, {
8108
- ref: popup,
8109
- initialFocus: popup,
8110
- finalFocus: returnFocus,
8307
+ ref: onPopupRef,
8308
+ initialFocus,
8309
+ finalFocus: () => dismissed.current ? props.returnFocus() : false,
8111
8310
  showClose: false,
8112
8311
  closeLabel,
8113
8312
  children: open && /* @__PURE__ */ jsxs(Fragment$1, { children: [
8114
- /* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: labels.title ?? t("credentialDisclosure.title") }), /* @__PURE__ */ jsx(DialogDescription, { children: labels.description ?? t("credentialDisclosure.description") })] }),
8115
- /* @__PURE__ */ jsxs(DialogBody, {
8313
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), /* @__PURE__ */ jsx(DialogDescription, { children: description })] }),
8314
+ /* @__PURE__ */ jsx(DialogBody, {
8116
8315
  className: "grid gap-4",
8117
- children: [/* @__PURE__ */ jsxs("dl", {
8118
- className: "grid grid-cols-2 gap-2 text-sm",
8119
- children: [
8120
- /* @__PURE__ */ jsx("dt", {
8121
- className: "text-muted-foreground",
8122
- children: labels.credentialIdLabel ?? t("credentialDisclosure.credentialId")
8123
- }),
8124
- /* @__PURE__ */ jsx("dd", {
8125
- className: "break-all font-mono",
8126
- children: disclosure.credentialId
8127
- }),
8128
- /* @__PURE__ */ jsx("dt", {
8129
- className: "text-muted-foreground",
8130
- children: labels.expiryLabel ?? t("credentialDisclosure.expiry")
8131
- }),
8132
- /* @__PURE__ */ jsx("dd", { children: disclosure.expiresAt })
8133
- ]
8134
- }), /* @__PURE__ */ jsxs(LiveAnnouncer, { children: [disclosure.variant === "stored" ? /* @__PURE__ */ jsx(SecretField, {
8135
- value: disclosure.secret,
8136
- copyable: false,
8137
- "aria-label": secretLabel,
8138
- showLabel: labels.showLabel ?? t("credentialDisclosure.show"),
8139
- hideLabel: labels.hideLabel ?? t("credentialDisclosure.hide")
8140
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Alert, {
8141
- role: "alert",
8142
- tone: "warning",
8143
- title: labels.warningTitle ?? t("credentialDisclosure.warningTitle"),
8144
- description: labels.warningText ?? t("credentialDisclosure.warningText")
8145
- }), /* @__PURE__ */ jsx(CodeBlock, {
8146
- code: disclosure.secret,
8147
- copyable: false,
8148
- label: secretLabel,
8149
- wrapToggle: false,
8150
- defaultWrap: true
8151
- })] }), /* @__PURE__ */ jsx(CopyButton, {
8152
- value: () => disclosure.secret,
8153
- display: "label",
8154
- label: labels.copyLabel ?? t("credentialDisclosure.copy"),
8155
- copiedLabel: labels.copiedLabel ?? t("credentialDisclosure.copied"),
8156
- announcement: labels.successAnnouncement ?? t("credentialDisclosure.success"),
8157
- failureLabel: labels.failureAnnouncement ?? t("credentialDisclosure.failure"),
8158
- failureAnnouncement: labels.failureAnnouncement ?? t("credentialDisclosure.failure"),
8159
- failurePoliteness: "polite"
8160
- })] })]
8316
+ children: body
8161
8317
  }),
8162
8318
  /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(DialogClose, {
8163
8319
  render: /* @__PURE__ */ jsx(Button, { variant: "secondary" }),
@@ -8167,24 +8323,13 @@ function DisclosureSession({ disclosure, onClose, resolveReturnFocus, fallbackFo
8167
8323
  })
8168
8324
  });
8169
8325
  }
8170
- /** Programmatic focus permits negative tabIndex, but never hidden, inert or disabled targets. */
8171
- function usableFocusTarget(element) {
8172
- if (!(element instanceof HTMLElement) || !element.isConnected || element === document.body) return false;
8173
- if (element.matches(":disabled, [disabled], [aria-disabled=\"true\"], input[type=\"hidden\"]")) return false;
8174
- for (let ancestor = element; ancestor; ancestor = ancestor.parentElement) {
8175
- if (ancestor.hidden || ancestor.hasAttribute("inert") || ancestor.getAttribute("aria-disabled") === "true") return false;
8176
- const style = getComputedStyle(ancestor);
8177
- if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse" || style.contentVisibility === "hidden") return false;
8178
- }
8179
- return element.tabIndex >= 0 || element.hasAttribute("tabindex") || element.isContentEditable;
8180
- }
8181
8326
  //#endregion
8182
8327
  //#region src/forms/CredentialDisclosureDialog/block.meta.ts
8183
8328
  const meta$13 = {
8184
8329
  name: "Credential disclosure dialog",
8185
8330
  family: "forms",
8186
8331
  status: "beta",
8187
- description: "Controlled disclosure for an existing credential. Set disclosure ({variant: stored | fresh, credentialId, expiresAt: caller-formatted ReactNode, secret}) only after a successful response; null renders nothing while pending or on failure. Clear disclosure to null in onClose to drop the secret; a fresh secret requires a new successful response before showing it again. No pending/error state or internal secret retention. Stored values start masked; each null-to-value opening or changed credentialId/secret resets masking and copy feedback. Fresh values use a non-copyable CodeBlock with a kit CopyButton and a replacement warning. Optional English defaults through en/es i18n for title, description, credentialIdLabel, expiryLabel, secretLabel, showLabel, hideLabel, copyLabel, copiedLabel, successAnnouncement, failureAnnouncement, warningTitle, warningText and closeLabel. Copy results are announced politely, including manual-selection fallback. Popup initial focus and focus trapping use Base UI; at close, resolveReturnFocus() is validated for connection, visibility, enabled state and focusability, then fallbackFocus(), then the original opener. Synthetic fixtures only; the default Stored preview is masked and the Fresh preview starts closed.",
8332
+ description: "Controlled disclosure for an existing credential. Set disclosure ({variant: stored | fresh, credentialId, expiresAt: caller-formatted ReactNode, secret}) only after a successful response; null renders nothing while pending or on failure. Clear disclosure, additionalSecrets and secret-bearing revealContent in onClose to drop every secret; a fresh secret requires a new successful response before showing it again. No pending/error state or internal secret retention. Stored values start masked; each null-to-value opening or changed credentialId/secret resets masking and copy feedback. Fresh values use a non-copyable CodeBlock with a kit CopyButton and a replacement warning, or a masked SecretField when maskFresh is set at session start. Optional additionalSecrets ({id, label, value}) are always masked and use independent toggles and serialized copy actions; ids must be non-empty, unique and not secret, with the first duplicate winning. Multi-secret groups announce label plus copiedLabel, or copiedAnnouncementFor(label); onCopy(id) never receives a value. Every secret and the optional revealContent slot have data-kit-redact. The session key includes additional ids and values. presentation is dialog by default or inline, fixed until the next opening: inline is a named section, never traps focus or handles Escape, and its warning is a non-live note. Surviving focus stays; removing the focused slot or session control focuses the popup or inline heading. Focused inline unmounts return focus through the same resolvers. Dismissed retained props stay hidden; clear and re-set or change a session value to reopen. Optional English defaults through en/es i18n for title, description, credentialIdLabel, expiryLabel, secretLabel, showLabel, hideLabel, copyLabel, copiedLabel, successAnnouncement, failureAnnouncement, warningTitle, warningText and closeLabel. Copy results are announced politely, including manual-selection fallback. Popup initial focus and focus trapping use Base UI; at close, resolveReturnFocus() is validated for connection, visibility, enabled state and focusability, then fallbackFocus(), then the original opener. Synthetic fixtures only; the default Stored preview is masked and the Fresh preview starts closed.",
8188
8333
  uses: [
8189
8334
  "Dialog",
8190
8335
  "SecretField",