@meetreeve/ui 0.14.0 → 0.15.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.mts CHANGED
@@ -649,7 +649,17 @@ declare function ContextSwitcher({ items, activeRefId, onSelect, footer, placeho
649
649
  * optional grant methods, so a pre-CD-5 client keeps working unchanged.
650
650
  */
651
651
  type ConnectableTransport = "composio" | "native";
652
+ /**
653
+ * The widest access an entry offers. "write" is retired (DEV-13661: no
654
+ * provider ever only acted, so its old label was never true); it stays in the
655
+ * union so an older server payload still type-checks, and it renders as
656
+ * "Read + write".
657
+ */
652
658
  type ConnectableCapability = "read" | "write" | "readwrite";
659
+ /** What a connection lets Reeve do: ingest/query, or also act (DEV-13661). */
660
+ type ConnectableAccess = "read" | "readwrite";
661
+ /** The ONE access -> words mapping; nothing is labeled as acting alone (DEV-13661). */
662
+ declare function accessLabel(access: ConnectableCapability): string;
653
663
  type ConnectionKind = "personal" | "org";
654
664
  type ConnectorGranteeType = "user" | "role";
655
665
  /** An RBAC role a grant can target, injected via the entry data (CD-2). */
@@ -806,7 +816,24 @@ interface ConnectableEntry {
806
816
  * picker never assumes an email shape or synthesizes this itself.
807
817
  */
808
818
  account_label?: string | null;
819
+ /**
820
+ * What this live connection lets Reeve do (DEV-13661). Absent on a
821
+ * pre-DEV-13661 payload, where the entry's `capability` stands in.
822
+ */
823
+ access?: ConnectableAccess | null;
809
824
  };
825
+ /**
826
+ * The accesses this entry may be connected as (DEV-13661), in
827
+ * ("read", "readwrite") order. More than one renders a Read / Read + write
828
+ * choice whose pick reaches `client.connect` as `opts.access`.
829
+ */
830
+ access_options?: ConnectableAccess[] | null;
831
+ /**
832
+ * Every connection provider key that belongs to this entry's provider
833
+ * (its own slug first), so a host can group stored connections onto one
834
+ * row per provider (DEV-13661).
835
+ */
836
+ provider_keys?: string[] | null;
810
837
  /** true = tenant has a live connection but the entry left the allowlist. */
811
838
  deprecated: boolean;
812
839
  /** Org-connect permission + role options for this entry (CD-4). */
@@ -841,9 +868,12 @@ interface ConnectorPickerClient {
841
868
  * composio -> open the returned connect_url; native may resolve with none.
842
869
  * `opts.kind` is additive (DEV-5654): a pre-CD-5 client declaring only
843
870
  * `connect(entry)` still satisfies this signature and simply ignores it.
871
+ * `opts.access` (DEV-13661) is the row's Read / Read + write pick, sent
872
+ * only when the entry offers a choice.
844
873
  */
845
874
  connect(entry: ConnectableEntry, opts?: {
846
875
  kind: ConnectionKind;
876
+ access?: ConnectableAccess;
847
877
  }): Promise<{
848
878
  connect_url?: string;
849
879
  }>;
@@ -895,4 +925,4 @@ interface ConnectorPickerProps {
895
925
  }
896
926
  declare function ConnectorPicker({ client, purpose, lens, layout, onConnected, }: ConnectorPickerProps): react.JSX.Element | null;
897
927
 
898
- export { type AppRungItem, AppSidebar, type AppSidebarBrand, type AppSidebarModuleDef, type AppSidebarProps, type AppSidebarTopItem, type Brand, BrandGlyph, type BrandGlyphProps, type ConnectableCapability, type ConnectableEntry, type ConnectableTransport, type ConnectionKind, type ConnectorConnectionHealth, type ConnectorCredentialField, type ConnectorCredentialTestResult, type ConnectorGrant, type ConnectorGrantInput, type ConnectorGranteeType, type ConnectorGuideStep, ConnectorPicker, type ConnectorPickerClient, type ConnectorPickerLens, type ConnectorPickerProps, type ConnectorRoleOption, type ConnectorSetupGuide, type ContextOrgGroup, ContextSwitcher, type ContextSwitcherItem, type ContextSwitcherProps, type FallbackModuleDef, Flyout, type FlyoutChild, type FlyoutProps, GLYPH_CANVAS_FILL, type GroupRungItem, type HeaderNavItem, InfoTip, type InfoTipProps, LOCKUP_GAP, LOCKUP_ROW, MARK_CAP_RATIO, MARK_WEIGHT, type OrgConnectPolicy, REEVE_ACCENT, ResponsiveHeader, type ResponsiveHeaderProps, type RungItem, type SidebarLadder, type SidebarLadderParsed, type SidebarRung, type UseLadderRungOptions, WORDMARK_WEIGHT, deriveFallbackLadder, faviconUrl, groupByOrg, reconcileLadder, resolveLadder, useLadderRung, useResponsivePlaceholder, zSidebarLadder };
928
+ export { type AppRungItem, AppSidebar, type AppSidebarBrand, type AppSidebarModuleDef, type AppSidebarProps, type AppSidebarTopItem, type Brand, BrandGlyph, type BrandGlyphProps, type ConnectableAccess, type ConnectableCapability, type ConnectableEntry, type ConnectableTransport, type ConnectionKind, type ConnectorConnectionHealth, type ConnectorCredentialField, type ConnectorCredentialTestResult, type ConnectorGrant, type ConnectorGrantInput, type ConnectorGranteeType, type ConnectorGuideStep, ConnectorPicker, type ConnectorPickerClient, type ConnectorPickerLens, type ConnectorPickerProps, type ConnectorRoleOption, type ConnectorSetupGuide, type ContextOrgGroup, ContextSwitcher, type ContextSwitcherItem, type ContextSwitcherProps, type FallbackModuleDef, Flyout, type FlyoutChild, type FlyoutProps, GLYPH_CANVAS_FILL, type GroupRungItem, type HeaderNavItem, InfoTip, type InfoTipProps, LOCKUP_GAP, LOCKUP_ROW, MARK_CAP_RATIO, MARK_WEIGHT, type OrgConnectPolicy, REEVE_ACCENT, ResponsiveHeader, type ResponsiveHeaderProps, type RungItem, type SidebarLadder, type SidebarLadderParsed, type SidebarRung, type UseLadderRungOptions, WORDMARK_WEIGHT, accessLabel, deriveFallbackLadder, faviconUrl, groupByOrg, reconcileLadder, resolveLadder, useLadderRung, useResponsivePlaceholder, zSidebarLadder };
package/dist/index.d.ts CHANGED
@@ -649,7 +649,17 @@ declare function ContextSwitcher({ items, activeRefId, onSelect, footer, placeho
649
649
  * optional grant methods, so a pre-CD-5 client keeps working unchanged.
650
650
  */
651
651
  type ConnectableTransport = "composio" | "native";
652
+ /**
653
+ * The widest access an entry offers. "write" is retired (DEV-13661: no
654
+ * provider ever only acted, so its old label was never true); it stays in the
655
+ * union so an older server payload still type-checks, and it renders as
656
+ * "Read + write".
657
+ */
652
658
  type ConnectableCapability = "read" | "write" | "readwrite";
659
+ /** What a connection lets Reeve do: ingest/query, or also act (DEV-13661). */
660
+ type ConnectableAccess = "read" | "readwrite";
661
+ /** The ONE access -> words mapping; nothing is labeled as acting alone (DEV-13661). */
662
+ declare function accessLabel(access: ConnectableCapability): string;
653
663
  type ConnectionKind = "personal" | "org";
654
664
  type ConnectorGranteeType = "user" | "role";
655
665
  /** An RBAC role a grant can target, injected via the entry data (CD-2). */
@@ -806,7 +816,24 @@ interface ConnectableEntry {
806
816
  * picker never assumes an email shape or synthesizes this itself.
807
817
  */
808
818
  account_label?: string | null;
819
+ /**
820
+ * What this live connection lets Reeve do (DEV-13661). Absent on a
821
+ * pre-DEV-13661 payload, where the entry's `capability` stands in.
822
+ */
823
+ access?: ConnectableAccess | null;
809
824
  };
825
+ /**
826
+ * The accesses this entry may be connected as (DEV-13661), in
827
+ * ("read", "readwrite") order. More than one renders a Read / Read + write
828
+ * choice whose pick reaches `client.connect` as `opts.access`.
829
+ */
830
+ access_options?: ConnectableAccess[] | null;
831
+ /**
832
+ * Every connection provider key that belongs to this entry's provider
833
+ * (its own slug first), so a host can group stored connections onto one
834
+ * row per provider (DEV-13661).
835
+ */
836
+ provider_keys?: string[] | null;
810
837
  /** true = tenant has a live connection but the entry left the allowlist. */
811
838
  deprecated: boolean;
812
839
  /** Org-connect permission + role options for this entry (CD-4). */
@@ -841,9 +868,12 @@ interface ConnectorPickerClient {
841
868
  * composio -> open the returned connect_url; native may resolve with none.
842
869
  * `opts.kind` is additive (DEV-5654): a pre-CD-5 client declaring only
843
870
  * `connect(entry)` still satisfies this signature and simply ignores it.
871
+ * `opts.access` (DEV-13661) is the row's Read / Read + write pick, sent
872
+ * only when the entry offers a choice.
844
873
  */
845
874
  connect(entry: ConnectableEntry, opts?: {
846
875
  kind: ConnectionKind;
876
+ access?: ConnectableAccess;
847
877
  }): Promise<{
848
878
  connect_url?: string;
849
879
  }>;
@@ -895,4 +925,4 @@ interface ConnectorPickerProps {
895
925
  }
896
926
  declare function ConnectorPicker({ client, purpose, lens, layout, onConnected, }: ConnectorPickerProps): react.JSX.Element | null;
897
927
 
898
- export { type AppRungItem, AppSidebar, type AppSidebarBrand, type AppSidebarModuleDef, type AppSidebarProps, type AppSidebarTopItem, type Brand, BrandGlyph, type BrandGlyphProps, type ConnectableCapability, type ConnectableEntry, type ConnectableTransport, type ConnectionKind, type ConnectorConnectionHealth, type ConnectorCredentialField, type ConnectorCredentialTestResult, type ConnectorGrant, type ConnectorGrantInput, type ConnectorGranteeType, type ConnectorGuideStep, ConnectorPicker, type ConnectorPickerClient, type ConnectorPickerLens, type ConnectorPickerProps, type ConnectorRoleOption, type ConnectorSetupGuide, type ContextOrgGroup, ContextSwitcher, type ContextSwitcherItem, type ContextSwitcherProps, type FallbackModuleDef, Flyout, type FlyoutChild, type FlyoutProps, GLYPH_CANVAS_FILL, type GroupRungItem, type HeaderNavItem, InfoTip, type InfoTipProps, LOCKUP_GAP, LOCKUP_ROW, MARK_CAP_RATIO, MARK_WEIGHT, type OrgConnectPolicy, REEVE_ACCENT, ResponsiveHeader, type ResponsiveHeaderProps, type RungItem, type SidebarLadder, type SidebarLadderParsed, type SidebarRung, type UseLadderRungOptions, WORDMARK_WEIGHT, deriveFallbackLadder, faviconUrl, groupByOrg, reconcileLadder, resolveLadder, useLadderRung, useResponsivePlaceholder, zSidebarLadder };
928
+ export { type AppRungItem, AppSidebar, type AppSidebarBrand, type AppSidebarModuleDef, type AppSidebarProps, type AppSidebarTopItem, type Brand, BrandGlyph, type BrandGlyphProps, type ConnectableAccess, type ConnectableCapability, type ConnectableEntry, type ConnectableTransport, type ConnectionKind, type ConnectorConnectionHealth, type ConnectorCredentialField, type ConnectorCredentialTestResult, type ConnectorGrant, type ConnectorGrantInput, type ConnectorGranteeType, type ConnectorGuideStep, ConnectorPicker, type ConnectorPickerClient, type ConnectorPickerLens, type ConnectorPickerProps, type ConnectorRoleOption, type ConnectorSetupGuide, type ContextOrgGroup, ContextSwitcher, type ContextSwitcherItem, type ContextSwitcherProps, type FallbackModuleDef, Flyout, type FlyoutChild, type FlyoutProps, GLYPH_CANVAS_FILL, type GroupRungItem, type HeaderNavItem, InfoTip, type InfoTipProps, LOCKUP_GAP, LOCKUP_ROW, MARK_CAP_RATIO, MARK_WEIGHT, type OrgConnectPolicy, REEVE_ACCENT, ResponsiveHeader, type ResponsiveHeaderProps, type RungItem, type SidebarLadder, type SidebarLadderParsed, type SidebarRung, type UseLadderRungOptions, WORDMARK_WEIGHT, accessLabel, deriveFallbackLadder, faviconUrl, groupByOrg, reconcileLadder, resolveLadder, useLadderRung, useResponsivePlaceholder, zSidebarLadder };
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ __export(src_exports, {
45
45
  REEVE_ACCENT: () => REEVE_ACCENT,
46
46
  ResponsiveHeader: () => ResponsiveHeader,
47
47
  WORDMARK_WEIGHT: () => WORDMARK_WEIGHT,
48
+ accessLabel: () => accessLabel,
48
49
  deriveFallbackLadder: () => deriveFallbackLadder,
49
50
  faviconUrl: () => faviconUrl,
50
51
  groupByOrg: () => groupByOrg,
@@ -1463,6 +1464,9 @@ function ContextSwitcher({
1463
1464
  // src/connector-picker.tsx
1464
1465
  var import_react9 = require("react");
1465
1466
  var import_jsx_runtime8 = require("react/jsx-runtime");
1467
+ function accessLabel(access) {
1468
+ return access === "read" ? "Read" : "Read + write";
1469
+ }
1466
1470
  var DEFAULT_CONNECT_ERROR = "Couldn't start the connection. Please try again.";
1467
1471
  var MAX_RECONNECT_REFOCUS_CHECKS = 3;
1468
1472
  var RECONNECT_UNCONFIRMED_ERROR = "Couldn't confirm the reconnect \u2014 the connection still reports unhealthy. Try again, or disconnect and reconnect.";
@@ -1484,10 +1488,17 @@ function hasUngroundedPlaceholder(text) {
1484
1488
  function isDevEnvironment() {
1485
1489
  return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1486
1490
  }
1487
- function capabilityLabel(capability) {
1488
- if (capability === "read") return "Read only";
1489
- if (capability === "write") return "Write only";
1490
- return "Read & write";
1491
+ function tileAccess(entry) {
1492
+ if (entry.connection.status === "connected" && entry.connection.access) {
1493
+ return entry.connection.access;
1494
+ }
1495
+ return entry.capability;
1496
+ }
1497
+ function accessChoices(entry) {
1498
+ const options = (entry.access_options ?? []).filter(
1499
+ (a) => a === "read" || a === "readwrite"
1500
+ );
1501
+ return options.length > 1 ? options : [];
1491
1502
  }
1492
1503
  function errorMessageFrom(err, fallback) {
1493
1504
  return err instanceof Error && err.message ? err.message : fallback;
@@ -1606,7 +1617,7 @@ function ConnectorPicker({
1606
1617
  };
1607
1618
  }, [gated, refreshAfterReturn]);
1608
1619
  const handleConnect = (0, import_react9.useCallback)(
1609
- async (entry, kind) => {
1620
+ async (entry, kind, access) => {
1610
1621
  if (isUnhealthy(entry)) {
1611
1622
  reconnectingRef.current.add(entry.slug);
1612
1623
  } else {
@@ -1615,7 +1626,10 @@ function ConnectorPicker({
1615
1626
  reconnectChecksRef.current.delete(entry.slug);
1616
1627
  setUiState((prev) => ({ ...prev, [entry.slug]: { kind: "connecting" } }));
1617
1628
  try {
1618
- const result = await clientRef.current.connect(entry, { kind });
1629
+ const result = await clientRef.current.connect(
1630
+ entry,
1631
+ access ? { kind, access } : { kind }
1632
+ );
1619
1633
  if (result?.connect_url) {
1620
1634
  window.open(result.connect_url, "_blank", "noopener,noreferrer");
1621
1635
  return;
@@ -1805,6 +1819,9 @@ function ConnectorTile({
1805
1819
  const errorMessage = ui?.kind === "error" ? ui.message : null;
1806
1820
  const unhealthy = isUnhealthy(entry);
1807
1821
  const [kind, setKind] = (0, import_react9.useState)("personal");
1822
+ const choices = accessChoices(entry);
1823
+ const [access, setAccess] = (0, import_react9.useState)(null);
1824
+ const connectAccess = choices.length ? access && choices.includes(access) ? access : choices[choices.length - 1] : void 0;
1808
1825
  const [sharingOpen, setSharingOpen] = (0, import_react9.useState)(false);
1809
1826
  const [setupOpen, setSetupOpen] = (0, import_react9.useState)(false);
1810
1827
  const credentialFields = entry.credential_fields ?? [];
@@ -1839,7 +1856,7 @@ function ConnectorTile({
1839
1856
  )
1840
1857
  ] }),
1841
1858
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1842
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-testid": `connector-capability-${entry.slug}`, children: capabilityLabel(entry.capability) }),
1859
+ (isConnected || choices.length === 0) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "data-testid": `connector-capability-${entry.slug}`, children: accessLabel(tileAccess(entry)) }),
1843
1860
  isConnected && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1844
1861
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": "true", children: "\xB7" }),
1845
1862
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
@@ -1891,6 +1908,32 @@ function ConnectorTile({
1891
1908
  children: attribution
1892
1909
  }
1893
1910
  ),
1911
+ !isConnected && choices.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1912
+ "div",
1913
+ {
1914
+ "data-testid": `connector-access-choice-${entry.slug}`,
1915
+ role: "group",
1916
+ "aria-label": "Access",
1917
+ className: "mt-1 flex items-center gap-1",
1918
+ children: choices.map((option) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1919
+ "button",
1920
+ {
1921
+ type: "button",
1922
+ "data-testid": `connector-access-${option}-${entry.slug}`,
1923
+ "aria-pressed": connectAccess === option,
1924
+ disabled: isConnecting,
1925
+ onClick: () => setAccess(option),
1926
+ className: cn(
1927
+ "rounded-full border px-2 py-0.5 text-[11px]",
1928
+ connectAccess === option ? "border-primary bg-primary/10 font-medium" : "border-border text-muted-foreground",
1929
+ "disabled:opacity-50"
1930
+ ),
1931
+ children: accessLabel(option)
1932
+ },
1933
+ option
1934
+ ))
1935
+ }
1936
+ ),
1894
1937
  !isConnected && orgPermitted && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1895
1938
  "div",
1896
1939
  {
@@ -1944,7 +1987,7 @@ function ConnectorTile({
1944
1987
  {
1945
1988
  type: "button",
1946
1989
  "data-testid": `connector-reconnect-${entry.slug}`,
1947
- onClick: () => onConnect(entry, entry.connection.kind ?? "personal"),
1990
+ onClick: () => onConnect(entry, entry.connection.kind ?? "personal", entry.connection.access ?? void 0),
1948
1991
  disabled: isConnecting || isDisconnecting,
1949
1992
  className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
1950
1993
  children: isConnecting ? "Reconnecting\u2026" : "Reconnect"
@@ -1983,7 +2026,7 @@ function ConnectorTile({
1983
2026
  {
1984
2027
  type: "button",
1985
2028
  "data-testid": `connector-connect-${entry.slug}`,
1986
- onClick: () => onConnect(entry, connectKind),
2029
+ onClick: () => onConnect(entry, connectKind, connectAccess),
1987
2030
  disabled: isConnecting,
1988
2031
  className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
1989
2032
  children: isConnecting ? "Connecting\u2026" : "Connect"
@@ -2556,6 +2599,7 @@ function ManageSharing({
2556
2599
  REEVE_ACCENT,
2557
2600
  ResponsiveHeader,
2558
2601
  WORDMARK_WEIGHT,
2602
+ accessLabel,
2559
2603
  deriveFallbackLadder,
2560
2604
  faviconUrl,
2561
2605
  groupByOrg,
package/dist/index.mjs CHANGED
@@ -1407,6 +1407,9 @@ function ContextSwitcher({
1407
1407
  // src/connector-picker.tsx
1408
1408
  import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "react";
1409
1409
  import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1410
+ function accessLabel(access) {
1411
+ return access === "read" ? "Read" : "Read + write";
1412
+ }
1410
1413
  var DEFAULT_CONNECT_ERROR = "Couldn't start the connection. Please try again.";
1411
1414
  var MAX_RECONNECT_REFOCUS_CHECKS = 3;
1412
1415
  var RECONNECT_UNCONFIRMED_ERROR = "Couldn't confirm the reconnect \u2014 the connection still reports unhealthy. Try again, or disconnect and reconnect.";
@@ -1428,10 +1431,17 @@ function hasUngroundedPlaceholder(text) {
1428
1431
  function isDevEnvironment() {
1429
1432
  return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
1430
1433
  }
1431
- function capabilityLabel(capability) {
1432
- if (capability === "read") return "Read only";
1433
- if (capability === "write") return "Write only";
1434
- return "Read & write";
1434
+ function tileAccess(entry) {
1435
+ if (entry.connection.status === "connected" && entry.connection.access) {
1436
+ return entry.connection.access;
1437
+ }
1438
+ return entry.capability;
1439
+ }
1440
+ function accessChoices(entry) {
1441
+ const options = (entry.access_options ?? []).filter(
1442
+ (a) => a === "read" || a === "readwrite"
1443
+ );
1444
+ return options.length > 1 ? options : [];
1435
1445
  }
1436
1446
  function errorMessageFrom(err, fallback) {
1437
1447
  return err instanceof Error && err.message ? err.message : fallback;
@@ -1550,7 +1560,7 @@ function ConnectorPicker({
1550
1560
  };
1551
1561
  }, [gated, refreshAfterReturn]);
1552
1562
  const handleConnect = useCallback4(
1553
- async (entry, kind) => {
1563
+ async (entry, kind, access) => {
1554
1564
  if (isUnhealthy(entry)) {
1555
1565
  reconnectingRef.current.add(entry.slug);
1556
1566
  } else {
@@ -1559,7 +1569,10 @@ function ConnectorPicker({
1559
1569
  reconnectChecksRef.current.delete(entry.slug);
1560
1570
  setUiState((prev) => ({ ...prev, [entry.slug]: { kind: "connecting" } }));
1561
1571
  try {
1562
- const result = await clientRef.current.connect(entry, { kind });
1572
+ const result = await clientRef.current.connect(
1573
+ entry,
1574
+ access ? { kind, access } : { kind }
1575
+ );
1563
1576
  if (result?.connect_url) {
1564
1577
  window.open(result.connect_url, "_blank", "noopener,noreferrer");
1565
1578
  return;
@@ -1749,6 +1762,9 @@ function ConnectorTile({
1749
1762
  const errorMessage = ui?.kind === "error" ? ui.message : null;
1750
1763
  const unhealthy = isUnhealthy(entry);
1751
1764
  const [kind, setKind] = useState7("personal");
1765
+ const choices = accessChoices(entry);
1766
+ const [access, setAccess] = useState7(null);
1767
+ const connectAccess = choices.length ? access && choices.includes(access) ? access : choices[choices.length - 1] : void 0;
1752
1768
  const [sharingOpen, setSharingOpen] = useState7(false);
1753
1769
  const [setupOpen, setSetupOpen] = useState7(false);
1754
1770
  const credentialFields = entry.credential_fields ?? [];
@@ -1783,7 +1799,7 @@ function ConnectorTile({
1783
1799
  )
1784
1800
  ] }),
1785
1801
  /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1786
- /* @__PURE__ */ jsx8("span", { "data-testid": `connector-capability-${entry.slug}`, children: capabilityLabel(entry.capability) }),
1802
+ (isConnected || choices.length === 0) && /* @__PURE__ */ jsx8("span", { "data-testid": `connector-capability-${entry.slug}`, children: accessLabel(tileAccess(entry)) }),
1787
1803
  isConnected && /* @__PURE__ */ jsxs7(Fragment4, { children: [
1788
1804
  /* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "\xB7" }),
1789
1805
  /* @__PURE__ */ jsxs7(
@@ -1835,6 +1851,32 @@ function ConnectorTile({
1835
1851
  children: attribution
1836
1852
  }
1837
1853
  ),
1854
+ !isConnected && choices.length > 0 && /* @__PURE__ */ jsx8(
1855
+ "div",
1856
+ {
1857
+ "data-testid": `connector-access-choice-${entry.slug}`,
1858
+ role: "group",
1859
+ "aria-label": "Access",
1860
+ className: "mt-1 flex items-center gap-1",
1861
+ children: choices.map((option) => /* @__PURE__ */ jsx8(
1862
+ "button",
1863
+ {
1864
+ type: "button",
1865
+ "data-testid": `connector-access-${option}-${entry.slug}`,
1866
+ "aria-pressed": connectAccess === option,
1867
+ disabled: isConnecting,
1868
+ onClick: () => setAccess(option),
1869
+ className: cn(
1870
+ "rounded-full border px-2 py-0.5 text-[11px]",
1871
+ connectAccess === option ? "border-primary bg-primary/10 font-medium" : "border-border text-muted-foreground",
1872
+ "disabled:opacity-50"
1873
+ ),
1874
+ children: accessLabel(option)
1875
+ },
1876
+ option
1877
+ ))
1878
+ }
1879
+ ),
1838
1880
  !isConnected && orgPermitted && /* @__PURE__ */ jsxs7(
1839
1881
  "div",
1840
1882
  {
@@ -1888,7 +1930,7 @@ function ConnectorTile({
1888
1930
  {
1889
1931
  type: "button",
1890
1932
  "data-testid": `connector-reconnect-${entry.slug}`,
1891
- onClick: () => onConnect(entry, entry.connection.kind ?? "personal"),
1933
+ onClick: () => onConnect(entry, entry.connection.kind ?? "personal", entry.connection.access ?? void 0),
1892
1934
  disabled: isConnecting || isDisconnecting,
1893
1935
  className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
1894
1936
  children: isConnecting ? "Reconnecting\u2026" : "Reconnect"
@@ -1927,7 +1969,7 @@ function ConnectorTile({
1927
1969
  {
1928
1970
  type: "button",
1929
1971
  "data-testid": `connector-connect-${entry.slug}`,
1930
- onClick: () => onConnect(entry, connectKind),
1972
+ onClick: () => onConnect(entry, connectKind, connectAccess),
1931
1973
  disabled: isConnecting,
1932
1974
  className: "shrink-0 rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground disabled:opacity-50",
1933
1975
  children: isConnecting ? "Connecting\u2026" : "Connect"
@@ -2499,6 +2541,7 @@ export {
2499
2541
  REEVE_ACCENT,
2500
2542
  ResponsiveHeader,
2501
2543
  WORDMARK_WEIGHT,
2544
+ accessLabel,
2502
2545
  deriveFallbackLadder,
2503
2546
  faviconUrl,
2504
2547
  groupByOrg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meetreeve/ui",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Shared brand-adaptive UI primitives for MindFortress frontends — min-size tokens, a favicon→logo→monogram BrandGlyph, a responsive header that keeps the search bar usable on mobile, and AppSidebar's viewport-driven auto-collapse ladder.",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/index.js",