@ai-matrx/sharing 0.1.0 → 0.1.2

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/react.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { ReactNode, ComponentType } from 'react';
3
3
  import { ConfirmOptions } from '@ai-matrx/kit/confirm-opener';
4
- import { ResourceType, PermissionWithDetails, PermissionLevel, ShareActionResult, SharingCopyContext, AccessSummary, ShownTo, WhoCanSee, LaneChoice, Permission, CheckPermissionOptions } from '@ai-matrx/sharing/core';
4
+ import { ResourceType, PermissionWithDetails, PermissionLevel, ShareActionResult, SharingCopyContext, AccessSummary, ShownTo, WhoCanSee, LaneChoice, ShareLink, Permission, CheckPermissionOptions } from '@ai-matrx/sharing/core';
5
5
  import { EntityTypeToken } from '@ai-matrx/associations';
6
6
 
7
7
  type LooseProps = {
@@ -342,12 +342,33 @@ interface WhoCanSeeThisProps {
342
342
  }
343
343
  declare function WhoCanSeeThis({ whoCanSee, canChange, onChoose, row, }: WhoCanSeeThisProps): react__default.JSX.Element | null;
344
344
 
345
+ /** One more URL a link opens at (e.g. a file's direct byte URL), shown with copy + open. */
346
+ interface ShareLinkExtraUrl {
347
+ key: string;
348
+ label: string;
349
+ sublabel?: string | undefined;
350
+ url: string;
351
+ /** Highlighted as the main URL to hand out. */
352
+ primary?: boolean | undefined;
353
+ }
345
354
  interface ShareLinkPanelProps {
346
355
  resourceType: ResourceType;
347
356
  resourceId: string;
348
357
  isOwner: boolean;
349
358
  /** Whether this resource type offers no-login links (admin policy). Hidden when false. */
350
359
  enabled?: boolean | undefined;
360
+ /** Ask permission, expiry and max uses before a link is created. */
361
+ linkSettings?: boolean | undefined;
362
+ /** Show each link's access, expiry and uses. */
363
+ showLinkDetails?: boolean | undefined;
364
+ /** An open-in-new-tab control on each link. */
365
+ showOpenLink?: boolean | undefined;
366
+ /** Extra URLs per link (a file's direct byte URL), each with copy + open. */
367
+ extraUrls?: ((link: ShareLink) => ShareLinkExtraUrl[]) | undefined;
368
+ /** The origin link URLs are built on; defaults to the app URL. */
369
+ appOrigin?: string | undefined;
370
+ /** Every fresh list read — lets a host keep its own store of links in step. */
371
+ onLinksChange?: ((links: ShareLink[]) => void) | undefined;
351
372
  }
352
373
  /**
353
374
  * "Anyone with the link" — mint / copy / revoke no-login share links.
@@ -357,7 +378,7 @@ interface ShareLinkPanelProps {
357
378
  * link-carries-everything path — distinct from publishing to the web (which
358
379
  * needs a public render route). Owner-only.
359
380
  */
360
- declare function ShareLinkPanel({ resourceType, resourceId, isOwner, enabled, }: ShareLinkPanelProps): react__default.JSX.Element | null;
381
+ declare function ShareLinkPanel({ resourceType, resourceId, isOwner, enabled, linkSettings, showLinkDetails, showOpenLink, extraUrls, appOrigin, onLinksChange, }: ShareLinkPanelProps): react__default.JSX.Element | null;
361
382
 
362
383
  type GrantableLevels = {
363
384
  loading: boolean;
@@ -776,4 +797,4 @@ declare function WhoCouldSeeOn({ recordId }: {
776
797
  recordId: string;
777
798
  }): react.JSX.Element;
778
799
 
779
- export { AccessSummaryPanel, type AccessSummaryPanelProps, type AccessSummaryState, AddEveryoneInOrg, type AddEveryoneInOrgProps, type GrantableLevels, GuestBadge, LANE_CLOSED_SAY, OrgAvailabilityNote, type OrgMemberPerson, OutsideSharePanel, type OutsideSharePanelProps, PermissionBadge, PermissionLevelDescription, PermissionsList, PublicAccessTab, PublicBadge, RowControls, type RowControlsProps, SearchEngineIndexedSwitch, SecureLinkPanel, ShareLinkPanel, ShareModal, type ShareOutcome, type ShareSheetConnection, type ShareSheetConnections, type ShareSheetOrganization, type ShareSheetToastOptions, type ShareSheetUi, ShareWithUserTab, type UseAccessSummaryArgs, type UseAccessSummaryResult, type UseShareResult, WhoCanSeeThis, type WhoCanSeeThisProps, WhoCouldSeeOn, anyoneReachWords, configureShareSheetUi, resetShareSheetCache, shareSheetUi, useAccessSummary, useCanAdmin, useCanEdit, useGrantableLevels, useIsOwner, usePermissionCheck, usePermissions, useResourcePermissions, useShare, useSharedWithMe, useSharing, useSharingStatus };
800
+ export { AccessSummaryPanel, type AccessSummaryPanelProps, type AccessSummaryState, AddEveryoneInOrg, type AddEveryoneInOrgProps, type GrantableLevels, GuestBadge, LANE_CLOSED_SAY, OrgAvailabilityNote, type OrgMemberPerson, OutsideSharePanel, type OutsideSharePanelProps, PermissionBadge, PermissionLevelDescription, PermissionsList, PublicAccessTab, PublicBadge, RowControls, type RowControlsProps, SearchEngineIndexedSwitch, SecureLinkPanel, type ShareLinkExtraUrl, ShareLinkPanel, type ShareLinkPanelProps, ShareModal, type ShareOutcome, type ShareSheetConnection, type ShareSheetConnections, type ShareSheetOrganization, type ShareSheetToastOptions, type ShareSheetUi, ShareWithUserTab, type UseAccessSummaryArgs, type UseAccessSummaryResult, type UseShareResult, WhoCanSeeThis, type WhoCanSeeThisProps, WhoCouldSeeOn, anyoneReachWords, configureShareSheetUi, resetShareSheetCache, shareSheetUi, useAccessSummary, useCanAdmin, useCanEdit, useGrantableLevels, useIsOwner, usePermissionCheck, usePermissions, useResourcePermissions, useShare, useSharedWithMe, useSharing, useSharingStatus };
package/dist/react.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import react__default, { ReactNode, ComponentType } from 'react';
3
3
  import { ConfirmOptions } from '@ai-matrx/kit/confirm-opener';
4
- import { ResourceType, PermissionWithDetails, PermissionLevel, ShareActionResult, SharingCopyContext, AccessSummary, ShownTo, WhoCanSee, LaneChoice, Permission, CheckPermissionOptions } from '@ai-matrx/sharing/core';
4
+ import { ResourceType, PermissionWithDetails, PermissionLevel, ShareActionResult, SharingCopyContext, AccessSummary, ShownTo, WhoCanSee, LaneChoice, ShareLink, Permission, CheckPermissionOptions } from '@ai-matrx/sharing/core';
5
5
  import { EntityTypeToken } from '@ai-matrx/associations';
6
6
 
7
7
  type LooseProps = {
@@ -342,12 +342,33 @@ interface WhoCanSeeThisProps {
342
342
  }
343
343
  declare function WhoCanSeeThis({ whoCanSee, canChange, onChoose, row, }: WhoCanSeeThisProps): react__default.JSX.Element | null;
344
344
 
345
+ /** One more URL a link opens at (e.g. a file's direct byte URL), shown with copy + open. */
346
+ interface ShareLinkExtraUrl {
347
+ key: string;
348
+ label: string;
349
+ sublabel?: string | undefined;
350
+ url: string;
351
+ /** Highlighted as the main URL to hand out. */
352
+ primary?: boolean | undefined;
353
+ }
345
354
  interface ShareLinkPanelProps {
346
355
  resourceType: ResourceType;
347
356
  resourceId: string;
348
357
  isOwner: boolean;
349
358
  /** Whether this resource type offers no-login links (admin policy). Hidden when false. */
350
359
  enabled?: boolean | undefined;
360
+ /** Ask permission, expiry and max uses before a link is created. */
361
+ linkSettings?: boolean | undefined;
362
+ /** Show each link's access, expiry and uses. */
363
+ showLinkDetails?: boolean | undefined;
364
+ /** An open-in-new-tab control on each link. */
365
+ showOpenLink?: boolean | undefined;
366
+ /** Extra URLs per link (a file's direct byte URL), each with copy + open. */
367
+ extraUrls?: ((link: ShareLink) => ShareLinkExtraUrl[]) | undefined;
368
+ /** The origin link URLs are built on; defaults to the app URL. */
369
+ appOrigin?: string | undefined;
370
+ /** Every fresh list read — lets a host keep its own store of links in step. */
371
+ onLinksChange?: ((links: ShareLink[]) => void) | undefined;
351
372
  }
352
373
  /**
353
374
  * "Anyone with the link" — mint / copy / revoke no-login share links.
@@ -357,7 +378,7 @@ interface ShareLinkPanelProps {
357
378
  * link-carries-everything path — distinct from publishing to the web (which
358
379
  * needs a public render route). Owner-only.
359
380
  */
360
- declare function ShareLinkPanel({ resourceType, resourceId, isOwner, enabled, }: ShareLinkPanelProps): react__default.JSX.Element | null;
381
+ declare function ShareLinkPanel({ resourceType, resourceId, isOwner, enabled, linkSettings, showLinkDetails, showOpenLink, extraUrls, appOrigin, onLinksChange, }: ShareLinkPanelProps): react__default.JSX.Element | null;
361
382
 
362
383
  type GrantableLevels = {
363
384
  loading: boolean;
@@ -776,4 +797,4 @@ declare function WhoCouldSeeOn({ recordId }: {
776
797
  recordId: string;
777
798
  }): react.JSX.Element;
778
799
 
779
- export { AccessSummaryPanel, type AccessSummaryPanelProps, type AccessSummaryState, AddEveryoneInOrg, type AddEveryoneInOrgProps, type GrantableLevels, GuestBadge, LANE_CLOSED_SAY, OrgAvailabilityNote, type OrgMemberPerson, OutsideSharePanel, type OutsideSharePanelProps, PermissionBadge, PermissionLevelDescription, PermissionsList, PublicAccessTab, PublicBadge, RowControls, type RowControlsProps, SearchEngineIndexedSwitch, SecureLinkPanel, ShareLinkPanel, ShareModal, type ShareOutcome, type ShareSheetConnection, type ShareSheetConnections, type ShareSheetOrganization, type ShareSheetToastOptions, type ShareSheetUi, ShareWithUserTab, type UseAccessSummaryArgs, type UseAccessSummaryResult, type UseShareResult, WhoCanSeeThis, type WhoCanSeeThisProps, WhoCouldSeeOn, anyoneReachWords, configureShareSheetUi, resetShareSheetCache, shareSheetUi, useAccessSummary, useCanAdmin, useCanEdit, useGrantableLevels, useIsOwner, usePermissionCheck, usePermissions, useResourcePermissions, useShare, useSharedWithMe, useSharing, useSharingStatus };
800
+ export { AccessSummaryPanel, type AccessSummaryPanelProps, type AccessSummaryState, AddEveryoneInOrg, type AddEveryoneInOrgProps, type GrantableLevels, GuestBadge, LANE_CLOSED_SAY, OrgAvailabilityNote, type OrgMemberPerson, OutsideSharePanel, type OutsideSharePanelProps, PermissionBadge, PermissionLevelDescription, PermissionsList, PublicAccessTab, PublicBadge, RowControls, type RowControlsProps, SearchEngineIndexedSwitch, SecureLinkPanel, type ShareLinkExtraUrl, ShareLinkPanel, type ShareLinkPanelProps, ShareModal, type ShareOutcome, type ShareSheetConnection, type ShareSheetConnections, type ShareSheetOrganization, type ShareSheetToastOptions, type ShareSheetUi, ShareWithUserTab, type UseAccessSummaryArgs, type UseAccessSummaryResult, type UseShareResult, WhoCanSeeThis, type WhoCanSeeThisProps, WhoCouldSeeOn, anyoneReachWords, configureShareSheetUi, resetShareSheetCache, shareSheetUi, useAccessSummary, useCanAdmin, useCanEdit, useGrantableLevels, useIsOwner, usePermissionCheck, usePermissions, useResourcePermissions, useShare, useSharedWithMe, useSharing, useSharingStatus };
package/dist/react.js CHANGED
@@ -1978,7 +1978,7 @@ var SOURCE_LABELS = {
1978
1978
  };
1979
1979
  var EMAIL_SHAPE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1980
1980
  async function lookupConnectionByEmail(typed) {
1981
- const { data } = await createClient2().rpc("lookup_user_by_email", { lookup_email: typed });
1981
+ const { data } = await createClient2().schema("users").rpc("lookup_user_by_email", { lookup_email: typed });
1982
1982
  const row = data?.[0];
1983
1983
  return row ? {
1984
1984
  user_id: row.user_id,
@@ -2105,7 +2105,7 @@ function ShareWithUserTab({
2105
2105
  targetUserId = selectedContact.user_id;
2106
2106
  } else {
2107
2107
  const client = createClient2();
2108
- const { data: lookupData, error: lookupError } = await client.rpc(
2108
+ const { data: lookupData, error: lookupError } = await client.schema("users").rpc(
2109
2109
  "lookup_user_by_email",
2110
2110
  { lookup_email: trimmedEmail }
2111
2111
  );
@@ -2840,7 +2840,9 @@ import { AlertTriangle as AlertTriangle3 } from "lucide-react";
2840
2840
  // src/react/sheet/ShareLinkPanel.tsx
2841
2841
  import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef2, useState as useState10 } from "react";
2842
2842
  import { Button as Button7 } from "@ai-matrx/design-system";
2843
- import { Input as Input3 } from "@ai-matrx/design-system/controls";
2843
+ import { DateTimeField as DateTimeField2, Input as Input3, Select as Select5 } from "@ai-matrx/design-system/controls";
2844
+ import { OutboundLink } from "@ai-matrx/kit/outbound-react";
2845
+ import { formatAbsoluteDate } from "@ai-matrx/kit/format";
2844
2846
  import {
2845
2847
  Link2 as Link22,
2846
2848
  Copy,
@@ -2850,7 +2852,8 @@ import {
2850
2852
  Plus,
2851
2853
  Eye as Eye2,
2852
2854
  MessageSquareText,
2853
- RefreshCw
2855
+ RefreshCw,
2856
+ ExternalLink
2854
2857
  } from "lucide-react";
2855
2858
  import {
2856
2859
  createShareLink,
@@ -2958,8 +2961,23 @@ function ShareLinkPanel({
2958
2961
  resourceType,
2959
2962
  resourceId,
2960
2963
  isOwner,
2961
- enabled = true
2964
+ enabled = true,
2965
+ linkSettings = false,
2966
+ showLinkDetails = false,
2967
+ showOpenLink = false,
2968
+ extraUrls,
2969
+ appOrigin,
2970
+ onLinksChange
2962
2971
  }) {
2972
+ const [permissionLevel, setPermissionLevel] = useState10("viewer");
2973
+ const [expiresAt, setExpiresAt] = useState10("");
2974
+ const [maxUses, setMaxUses] = useState10("");
2975
+ const onLinksChangeRef = useRef2(onLinksChange);
2976
+ onLinksChangeRef.current = onLinksChange;
2977
+ const urlFor = useCallback5(
2978
+ (link) => shareUrlForLink(link, appOrigin),
2979
+ [appOrigin]
2980
+ );
2963
2981
  const { toast } = useToast();
2964
2982
  const [links, setLinks] = useState10([]);
2965
2983
  const [loading, setLoading] = useState10(true);
@@ -2975,10 +2993,11 @@ function ShareLinkPanel({
2975
2993
  const [handoffBusy, setHandoffBusy] = useState10(false);
2976
2994
  const { share, copy: copyMessage, fallbackDialog } = useShare();
2977
2995
  const resourceKey = `${resourceType}\0${resourceId}`;
2978
- const fetchLinks = useCallback5(
2979
- () => listShareLinks(resourceType, resourceId),
2980
- [resourceType, resourceId]
2981
- );
2996
+ const fetchLinks = useCallback5(async () => {
2997
+ const fresh = await listShareLinks(resourceType, resourceId);
2998
+ onLinksChangeRef.current?.(fresh);
2999
+ return fresh;
3000
+ }, [resourceType, resourceId]);
2982
3001
  const refresh = useCallback5(async () => {
2983
3002
  setLoading(true);
2984
3003
  setShareError(null);
@@ -3051,7 +3070,7 @@ function ShareLinkPanel({
3051
3070
  });
3052
3071
  return;
3053
3072
  }
3054
- const outcome = await copyMessage(shareUrlForLink(currentLink), {
3073
+ const outcome = await copyMessage(urlFor(currentLink), {
3055
3074
  title: "Copy link",
3056
3075
  description: "Copy this link and send it to the person you want to share with."
3057
3076
  });
@@ -3064,11 +3083,36 @@ function ShareLinkPanel({
3064
3083
  });
3065
3084
  }
3066
3085
  },
3067
- [copyMessage, refresh, toast]
3086
+ [copyMessage, refresh, toast, urlFor]
3087
+ );
3088
+ const copyExtraUrl = useCallback5(
3089
+ async (key, url) => {
3090
+ const outcome = await copyMessage(url, {
3091
+ title: "Copy link",
3092
+ description: "Copy this link and send it to the person you want to share with."
3093
+ });
3094
+ if (outcome === "copied") {
3095
+ setCopiedId(key);
3096
+ setTimeout(() => setCopiedId((k) => k === key ? null : k), 2e3);
3097
+ }
3098
+ },
3099
+ [copyMessage]
3068
3100
  );
3069
3101
  const createLinkOnce = useCallback5(async () => {
3070
- const result = await createShareLink({ resourceType, resourceId });
3102
+ const result = await createShareLink(
3103
+ linkSettings ? {
3104
+ resourceType,
3105
+ resourceId,
3106
+ permissionLevel,
3107
+ expiresAt: expiresAt ? new Date(expiresAt).toISOString() : null,
3108
+ maxUses: maxUses ? Number(maxUses) : null
3109
+ } : { resourceType, resourceId }
3110
+ );
3071
3111
  if (result.success && result.url) {
3112
+ if (linkSettings) {
3113
+ setExpiresAt("");
3114
+ setMaxUses("");
3115
+ }
3072
3116
  const freshLinks = await refresh();
3073
3117
  const createdLink = result.token && freshLinks ? freshLinks.find((link) => link.token === result.token) : void 0;
3074
3118
  if (!createdLink || !freshLinks) {
@@ -3087,7 +3131,17 @@ function ShareLinkPanel({
3087
3131
  variant: "destructive"
3088
3132
  });
3089
3133
  }
3090
- }, [resourceType, resourceId, refresh, copyLink, toast]);
3134
+ }, [
3135
+ resourceType,
3136
+ resourceId,
3137
+ refresh,
3138
+ copyLink,
3139
+ toast,
3140
+ linkSettings,
3141
+ permissionLevel,
3142
+ expiresAt,
3143
+ maxUses
3144
+ ]);
3091
3145
  const creatingRef = useRef2(false);
3092
3146
  const handleCreate = useCallback5(async () => {
3093
3147
  if (creatingRef.current) return;
@@ -3198,7 +3252,7 @@ function ShareLinkPanel({
3198
3252
  });
3199
3253
  return;
3200
3254
  }
3201
- const message = createSafeShareMessage(shareUrlForLink(currentLink));
3255
+ const message = createSafeShareMessage(urlFor(currentLink));
3202
3256
  const outcome = await share({
3203
3257
  title: "AI Matrx share",
3204
3258
  text: message,
@@ -3223,7 +3277,7 @@ function ShareLinkPanel({
3223
3277
  } finally {
3224
3278
  setHandoffBusy(false);
3225
3279
  }
3226
- }, [shareTarget, refresh, share, toast]);
3280
+ }, [shareTarget, refresh, share, toast, urlFor]);
3227
3281
  const handleCopyReviewedMessage = useCallback5(async () => {
3228
3282
  if (!shareTarget) return;
3229
3283
  setHandoffBusy(true);
@@ -3266,7 +3320,7 @@ function ShareLinkPanel({
3266
3320
  });
3267
3321
  return;
3268
3322
  }
3269
- const message = createSafeShareMessage(shareUrlForLink(currentLink));
3323
+ const message = createSafeShareMessage(urlFor(currentLink));
3270
3324
  const outcome = await copyMessage(message, {
3271
3325
  title: "Copy message",
3272
3326
  description: "Copy the reviewed message below and paste it into your messaging app."
@@ -3281,7 +3335,7 @@ function ShareLinkPanel({
3281
3335
  } finally {
3282
3336
  setHandoffBusy(false);
3283
3337
  }
3284
- }, [shareTarget, refresh, copyMessage, toast]);
3338
+ }, [shareTarget, refresh, copyMessage, toast, urlFor]);
3285
3339
  if (!isOwner) return null;
3286
3340
  if (!enabled) {
3287
3341
  return /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2 p-3 bg-muted/30 rounded-lg border", children: [
@@ -3315,79 +3369,164 @@ function ShareLinkPanel({
3315
3369
  }
3316
3370
  )
3317
3371
  ] }),
3372
+ linkSettings ? /* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-3", children: [
3373
+ /* @__PURE__ */ jsxs10("label", { className: "flex flex-col gap-1 text-xs", children: [
3374
+ /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Permission" }),
3375
+ /* @__PURE__ */ jsx14(
3376
+ Select5,
3377
+ {
3378
+ value: permissionLevel,
3379
+ onValueChange: setPermissionLevel,
3380
+ "aria-label": "Link permission",
3381
+ options: [
3382
+ { value: "viewer", label: "Read only" },
3383
+ { value: "editor", label: "Read + write" }
3384
+ ]
3385
+ }
3386
+ )
3387
+ ] }),
3388
+ /* @__PURE__ */ jsxs10("label", { className: "flex flex-col gap-1 text-xs", children: [
3389
+ /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Expires" }),
3390
+ /* @__PURE__ */ jsx14(DateTimeField2, { value: expiresAt, onValueChange: (v) => setExpiresAt(v ?? "") })
3391
+ ] }),
3392
+ /* @__PURE__ */ jsxs10("label", { className: "flex flex-col gap-1 text-xs", children: [
3393
+ /* @__PURE__ */ jsx14("span", { className: "text-muted-foreground", children: "Max uses" }),
3394
+ /* @__PURE__ */ jsx14(
3395
+ Input3,
3396
+ {
3397
+ type: "number",
3398
+ min: 1,
3399
+ "aria-label": "Max uses",
3400
+ value: maxUses,
3401
+ onChange: (e) => setMaxUses(e.target.value)
3402
+ }
3403
+ )
3404
+ ] })
3405
+ ] }) : null,
3318
3406
  isLoading ? /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-center py-3 text-xs text-muted-foreground", children: [
3319
3407
  /* @__PURE__ */ jsx14(Loader27, { className: "w-3.5 h-3.5 animate-spin mr-2" }),
3320
3408
  "Loading links\u2026"
3321
3409
  ] }) : activeLinks.length === 0 && !shareError ? /* @__PURE__ */ jsx14("p", { className: "text-xs text-muted-foreground py-1", children: "No share links yet. Create one to share this with anyone." }) : /* @__PURE__ */ jsx14("div", { className: "space-y-1.5", children: activeLinks.map((link) => {
3322
3410
  const unavailableReason = shareLinkUnavailableReason(link);
3323
- return /* @__PURE__ */ jsxs10(
3324
- "div",
3325
- {
3326
- className: "flex items-center gap-1.5 rounded-md border bg-background p-1.5",
3327
- children: [
3328
- /* @__PURE__ */ jsx14(
3329
- Input3,
3330
- {
3331
- mono: true,
3332
- readOnly: true,
3333
- value: shareUrlForLink(link),
3334
- className: "min-w-0 flex-1",
3335
- onFocus: (e) => e.currentTarget.select()
3336
- }
3337
- ),
3338
- /* @__PURE__ */ jsxs10(
3339
- "span",
3340
- {
3341
- className: "flex items-center gap-1 text-[10px] text-muted-foreground px-1",
3342
- title: `${link.useCount} view${link.useCount === 1 ? "" : "s"}`,
3343
- children: [
3344
- /* @__PURE__ */ jsx14(Eye2, { className: "w-3 h-3" }),
3345
- link.useCount
3346
- ]
3347
- }
3348
- ),
3349
- /* @__PURE__ */ jsx14(
3350
- Button7,
3351
- {
3352
- variant: "ghost",
3353
- size: "icon",
3354
- className: "h-11 w-11 flex-shrink-0 sm:h-8 sm:w-8",
3355
- onClick: () => copyLink(link),
3356
- disabled: !!unavailableReason,
3357
- title: unavailableReason ?? "Copy link",
3358
- "aria-label": "Copy share link",
3359
- children: copiedId === link.id ? /* @__PURE__ */ jsx14(Check2, { className: "w-4 h-4 text-green-500" }) : /* @__PURE__ */ jsx14(Copy, { className: "w-4 h-4" })
3360
- }
3361
- ),
3362
- /* @__PURE__ */ jsx14(
3363
- Button7,
3364
- {
3365
- variant: "ghost",
3366
- size: "icon",
3367
- className: "h-11 w-11 flex-shrink-0 sm:h-8 sm:w-8",
3368
- onClick: () => prepareTextShare(link.id),
3369
- disabled: !!unavailableReason || refreshing,
3370
- title: unavailableReason ?? "Review before sharing by text",
3371
- "aria-label": "Share by text",
3372
- children: refreshing ? /* @__PURE__ */ jsx14(Loader27, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ jsx14(MessageSquareText, { className: "w-4 h-4" })
3373
- }
3374
- ),
3375
- /* @__PURE__ */ jsx14(
3376
- Button7,
3377
- {
3378
- variant: "ghost",
3379
- size: "icon",
3380
- className: "h-11 w-11 flex-shrink-0 text-destructive sm:h-8 sm:w-8",
3381
- onClick: () => setRevokeTarget(link),
3382
- title: "Turn off link",
3383
- "aria-label": "Turn off share link",
3384
- children: /* @__PURE__ */ jsx14(Trash22, { className: "w-4 h-4" })
3385
- }
3386
- )
3387
- ]
3388
- },
3389
- link.id
3390
- );
3411
+ const linkUrl = urlFor(link);
3412
+ const extras = extraUrls?.(link) ?? [];
3413
+ return /* @__PURE__ */ jsxs10("div", { className: "space-y-1", children: [
3414
+ showLinkDetails ? /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-1.5 px-0.5 text-[11px] text-muted-foreground", children: [
3415
+ /* @__PURE__ */ jsx14("span", { className: "rounded bg-muted px-1.5 py-0.5 font-medium text-foreground", children: link.permissionLevel === "viewer" ? "Read only" : "Read + write" }),
3416
+ /* @__PURE__ */ jsx14("span", { children: link.expiresAt ? `Expires ${formatAbsoluteDate(link.expiresAt)}` : "No expiry" }),
3417
+ link.maxUses ? /* @__PURE__ */ jsx14("span", { children: `${link.useCount}/${link.maxUses} uses` }) : null
3418
+ ] }) : null,
3419
+ extras.map((extra) => /* @__PURE__ */ jsxs10(
3420
+ "div",
3421
+ {
3422
+ className: extra.primary ? "flex items-center gap-1.5 rounded-md border border-primary/30 bg-primary/5 p-1.5" : "flex items-center gap-1.5 rounded-md border bg-background p-1.5",
3423
+ children: [
3424
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-1 flex-col px-1", children: [
3425
+ /* @__PURE__ */ jsxs10("span", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: [
3426
+ extra.label,
3427
+ extra.sublabel ? ` \xB7 ${extra.sublabel}` : ""
3428
+ ] }),
3429
+ /* @__PURE__ */ jsx14("span", { className: "truncate font-mono text-xs", title: extra.url, children: extra.url })
3430
+ ] }),
3431
+ /* @__PURE__ */ jsx14(
3432
+ OutboundLink,
3433
+ {
3434
+ href: extra.url,
3435
+ "aria-label": `Open ${extra.label}`,
3436
+ title: "Open in new tab",
3437
+ className: "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground sm:h-8 sm:w-8",
3438
+ children: /* @__PURE__ */ jsx14(ExternalLink, { className: "w-4 h-4" })
3439
+ }
3440
+ ),
3441
+ /* @__PURE__ */ jsx14(
3442
+ Button7,
3443
+ {
3444
+ variant: "ghost",
3445
+ size: "icon",
3446
+ className: "h-11 w-11 flex-shrink-0 sm:h-8 sm:w-8",
3447
+ onClick: () => copyExtraUrl(`${link.id}:${extra.key}`, extra.url),
3448
+ disabled: !!unavailableReason,
3449
+ title: unavailableReason ?? "Copy",
3450
+ "aria-label": `Copy ${extra.label}`,
3451
+ children: copiedId === `${link.id}:${extra.key}` ? /* @__PURE__ */ jsx14(Check2, { className: "w-4 h-4 text-green-500" }) : /* @__PURE__ */ jsx14(Copy, { className: "w-4 h-4" })
3452
+ }
3453
+ )
3454
+ ]
3455
+ },
3456
+ extra.key
3457
+ )),
3458
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 rounded-md border bg-background p-1.5", children: [
3459
+ /* @__PURE__ */ jsx14(
3460
+ Input3,
3461
+ {
3462
+ mono: true,
3463
+ readOnly: true,
3464
+ value: linkUrl,
3465
+ className: "min-w-0 flex-1",
3466
+ onFocus: (e) => e.currentTarget.select()
3467
+ }
3468
+ ),
3469
+ /* @__PURE__ */ jsxs10(
3470
+ "span",
3471
+ {
3472
+ className: "flex items-center gap-1 text-[10px] text-muted-foreground px-1",
3473
+ title: `${link.useCount} view${link.useCount === 1 ? "" : "s"}`,
3474
+ children: [
3475
+ /* @__PURE__ */ jsx14(Eye2, { className: "w-3 h-3" }),
3476
+ link.useCount
3477
+ ]
3478
+ }
3479
+ ),
3480
+ showOpenLink ? /* @__PURE__ */ jsx14(
3481
+ OutboundLink,
3482
+ {
3483
+ href: linkUrl,
3484
+ "aria-label": "Open share link",
3485
+ title: "Open in new tab",
3486
+ className: "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground sm:h-8 sm:w-8",
3487
+ children: /* @__PURE__ */ jsx14(ExternalLink, { className: "w-4 h-4" })
3488
+ }
3489
+ ) : null,
3490
+ /* @__PURE__ */ jsx14(
3491
+ Button7,
3492
+ {
3493
+ variant: "ghost",
3494
+ size: "icon",
3495
+ className: "h-11 w-11 flex-shrink-0 sm:h-8 sm:w-8",
3496
+ onClick: () => copyLink(link),
3497
+ disabled: !!unavailableReason,
3498
+ title: unavailableReason ?? "Copy link",
3499
+ "aria-label": "Copy share link",
3500
+ children: copiedId === link.id ? /* @__PURE__ */ jsx14(Check2, { className: "w-4 h-4 text-green-500" }) : /* @__PURE__ */ jsx14(Copy, { className: "w-4 h-4" })
3501
+ }
3502
+ ),
3503
+ /* @__PURE__ */ jsx14(
3504
+ Button7,
3505
+ {
3506
+ variant: "ghost",
3507
+ size: "icon",
3508
+ className: "h-11 w-11 flex-shrink-0 sm:h-8 sm:w-8",
3509
+ onClick: () => prepareTextShare(link.id),
3510
+ disabled: !!unavailableReason || refreshing,
3511
+ title: unavailableReason ?? "Review before sharing by text",
3512
+ "aria-label": "Share by text",
3513
+ children: refreshing ? /* @__PURE__ */ jsx14(Loader27, { className: "w-4 h-4 animate-spin" }) : /* @__PURE__ */ jsx14(MessageSquareText, { className: "w-4 h-4" })
3514
+ }
3515
+ ),
3516
+ /* @__PURE__ */ jsx14(
3517
+ Button7,
3518
+ {
3519
+ variant: "ghost",
3520
+ size: "icon",
3521
+ className: "h-11 w-11 flex-shrink-0 text-destructive sm:h-8 sm:w-8",
3522
+ onClick: () => setRevokeTarget(link),
3523
+ title: "Turn off link",
3524
+ "aria-label": "Turn off share link",
3525
+ children: /* @__PURE__ */ jsx14(Trash22, { className: "w-4 h-4" })
3526
+ }
3527
+ )
3528
+ ] })
3529
+ ] }, link.id);
3391
3530
  }) }),
3392
3531
  shareError ? /* @__PURE__ */ jsxs10(
3393
3532
  "div",
@@ -3439,7 +3578,7 @@ function ShareLinkPanel({
3439
3578
  ] }),
3440
3579
  /* @__PURE__ */ jsxs10("div", { className: "space-y-1", children: [
3441
3580
  /* @__PURE__ */ jsx14("p", { className: "text-xs font-medium", children: "Message preview" }),
3442
- /* @__PURE__ */ jsx14("p", { className: "rounded-md border bg-muted/30 p-3 break-words", children: createSafeShareMessage(shareUrlForLink(shareTarget)) })
3581
+ /* @__PURE__ */ jsx14("p", { className: "rounded-md border bg-muted/30 p-3 break-words", children: createSafeShareMessage(urlFor(shareTarget)) })
3443
3582
  ] }),
3444
3583
  /* @__PURE__ */ jsxs10(
3445
3584
  Button7,