@page-speed/agent-everywhere 1.0.0 → 1.1.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.cjs CHANGED
@@ -9949,6 +9949,813 @@ function AudioPlayer({
9949
9949
  }) })
9950
9950
  ] });
9951
9951
  }
9952
+ var ICON_MAP = {
9953
+ globe: lucideReact.Globe,
9954
+ facebook: lucideReact.Facebook,
9955
+ instagram: lucideReact.Instagram,
9956
+ twitter: lucideReact.Twitter,
9957
+ x: lucideReact.Twitter,
9958
+ linkedin: lucideReact.Linkedin,
9959
+ youtube: lucideReact.Youtube,
9960
+ github: lucideReact.Github,
9961
+ mail: lucideReact.Mail,
9962
+ email: lucideReact.Mail,
9963
+ phone: lucideReact.Phone,
9964
+ link: lucideReact.Link,
9965
+ url: lucideReact.Link,
9966
+ image: lucideReact.Image,
9967
+ photo: lucideReact.Image,
9968
+ video: lucideReact.Video,
9969
+ star: lucideReact.Star,
9970
+ heart: lucideReact.Heart,
9971
+ check: lucideReact.Check,
9972
+ settings: lucideReact.Settings,
9973
+ user: lucideReact.User,
9974
+ account: lucideReact.User,
9975
+ calendar: lucideReact.Calendar,
9976
+ "map-pin": lucideReact.MapPin,
9977
+ mappin: lucideReact.MapPin,
9978
+ location: lucideReact.MapPin,
9979
+ music: lucideReact.Music,
9980
+ camera: lucideReact.Camera,
9981
+ message: lucideReact.MessageCircle,
9982
+ chat: lucideReact.MessageCircle,
9983
+ cart: lucideReact.ShoppingCart,
9984
+ shop: lucideReact.ShoppingCart
9985
+ };
9986
+ function resolveIconName(name, className = "size-5 shrink-0") {
9987
+ if (!name) return null;
9988
+ const Icon = ICON_MAP[name.trim().toLowerCase()];
9989
+ return Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { "aria-hidden": "true", className }) : null;
9990
+ }
9991
+ var DEFAULT_OPEN_GRAPH_ENDPOINT = "/api/opengraph";
9992
+ function firstString(...values) {
9993
+ for (const value of values) {
9994
+ if (typeof value === "string" && value.trim()) {
9995
+ return value.trim();
9996
+ }
9997
+ }
9998
+ return null;
9999
+ }
10000
+ function imageUrlFromOpenGraph(image) {
10001
+ if (!image) return null;
10002
+ if (typeof image === "string") return firstString(image);
10003
+ return firstString(image.url);
10004
+ }
10005
+ function domainFromUrl(url) {
10006
+ try {
10007
+ return new URL(url).hostname.replace(/^www\./, "");
10008
+ } catch {
10009
+ return url;
10010
+ }
10011
+ }
10012
+ function normalizeUrlForPreview(input) {
10013
+ const trimmed = input.trim();
10014
+ if (!trimmed) return "";
10015
+ return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
10016
+ }
10017
+ function buildEndpointUrl(endpoint, url) {
10018
+ const separator = endpoint.includes("?") ? "&" : "?";
10019
+ return `${endpoint}${separator}url=${encodeURIComponent(url)}`;
10020
+ }
10021
+ function getErrorMessage(body) {
10022
+ if (body && typeof body === "object" && "error" in body && typeof body.error === "string") {
10023
+ return body.error;
10024
+ }
10025
+ return "Could not load a preview for that link.";
10026
+ }
10027
+ function extractLinkPreview(data, fallbackUrl = "") {
10028
+ const hybridGraph = data.hybridGraph;
10029
+ const openGraph = data.openGraph;
10030
+ const htmlInferred = data.htmlInferred;
10031
+ const url = firstString(
10032
+ data.finalUrl,
10033
+ data.url,
10034
+ data.normalizedUrl,
10035
+ data.requestedUrl,
10036
+ openGraph?.url,
10037
+ hybridGraph?.url,
10038
+ htmlInferred?.url
10039
+ ) ?? normalizeUrlForPreview(fallbackUrl);
10040
+ return {
10041
+ url,
10042
+ domain: domainFromUrl(url),
10043
+ title: firstString(hybridGraph?.title, openGraph?.title, htmlInferred?.title),
10044
+ description: firstString(
10045
+ hybridGraph?.description,
10046
+ openGraph?.description,
10047
+ htmlInferred?.description
10048
+ ),
10049
+ image: firstString(
10050
+ hybridGraph?.image,
10051
+ imageUrlFromOpenGraph(openGraph?.image ?? null),
10052
+ htmlInferred?.image,
10053
+ htmlInferred?.images?.[0]
10054
+ ),
10055
+ favicon: firstString(hybridGraph?.favicon, htmlInferred?.favicon),
10056
+ siteName: firstString(
10057
+ htmlInferred?.site_name,
10058
+ openGraph?.site_name,
10059
+ hybridGraph?.site_name
10060
+ )
10061
+ };
10062
+ }
10063
+ async function fetchOpenGraphPreview(url, endpoint = DEFAULT_OPEN_GRAPH_ENDPOINT) {
10064
+ const response = await fetch(buildEndpointUrl(endpoint, url), {
10065
+ headers: { Accept: "application/json" }
10066
+ });
10067
+ let body = null;
10068
+ try {
10069
+ body = await response.json();
10070
+ } catch {
10071
+ body = null;
10072
+ }
10073
+ if (!response.ok) {
10074
+ throw new Error(getErrorMessage(body));
10075
+ }
10076
+ return body;
10077
+ }
10078
+ function LinkInput({
10079
+ label = "Add a link",
10080
+ icon,
10081
+ iconName,
10082
+ placeholder = "Paste or type a URL...",
10083
+ defaultUrl = "",
10084
+ defaultValue = null,
10085
+ value,
10086
+ defaultExpanded = false,
10087
+ expanded: controlledExpanded,
10088
+ disabled = false,
10089
+ endpoint = DEFAULT_OPEN_GRAPH_ENDPOINT,
10090
+ fetcher,
10091
+ onSubmit,
10092
+ onValueChange,
10093
+ onClear,
10094
+ onExpandedChange,
10095
+ className
10096
+ }) {
10097
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React4.useState(defaultExpanded);
10098
+ const [url, setUrl] = React4.useState(defaultUrl || defaultValue?.url || "");
10099
+ const [submittedUrl, setSubmittedUrl] = React4.useState(
10100
+ defaultUrl || defaultValue?.url || ""
10101
+ );
10102
+ const [status, setStatus] = React4.useState("idle");
10103
+ const [error, setError] = React4.useState(null);
10104
+ const [internalPreview, setInternalPreview] = React4.useState(
10105
+ defaultValue
10106
+ );
10107
+ const inputRef = React4.useRef(null);
10108
+ const reactId = React4.useId();
10109
+ const inputId = `link-input-${reactId}`;
10110
+ const errorId = `link-input-error-${reactId}`;
10111
+ const isExpanded = controlledExpanded === void 0 ? uncontrolledExpanded : controlledExpanded;
10112
+ const isPreviewControlled = value !== void 0;
10113
+ const preview = (isPreviewControlled ? value : internalPreview) ?? null;
10114
+ const isLoading = status === "loading";
10115
+ const hasPreview = preview !== null;
10116
+ const resolvedIcon = icon ?? resolveIconName(iconName, "size-5") ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { "aria-hidden": true, className: "size-5" });
10117
+ const resolvedFetcher = React4.useCallback(
10118
+ (nextUrl) => fetcher?.(nextUrl) ?? fetchOpenGraphPreview(nextUrl, endpoint),
10119
+ [endpoint, fetcher]
10120
+ );
10121
+ const setExpanded = React4.useCallback(
10122
+ (next) => {
10123
+ if (controlledExpanded === void 0) {
10124
+ setUncontrolledExpanded(next);
10125
+ }
10126
+ onExpandedChange?.(next);
10127
+ },
10128
+ [controlledExpanded, onExpandedChange]
10129
+ );
10130
+ React4.useEffect(() => {
10131
+ if (value !== void 0) {
10132
+ const nextUrl = value?.url ?? "";
10133
+ setSubmittedUrl(nextUrl);
10134
+ if (!isExpanded) setUrl(nextUrl);
10135
+ }
10136
+ }, [isExpanded, value]);
10137
+ React4.useEffect(() => {
10138
+ if (!isExpanded) return;
10139
+ const timeout = window.setTimeout(() => inputRef.current?.focus(), 60);
10140
+ return () => window.clearTimeout(timeout);
10141
+ }, [isExpanded]);
10142
+ async function handleSubmit(event) {
10143
+ event?.preventDefault();
10144
+ const nextUrl = url.trim();
10145
+ if (!nextUrl || isLoading || disabled) return;
10146
+ setStatus("loading");
10147
+ setError(null);
10148
+ try {
10149
+ const data = await resolvedFetcher(nextUrl);
10150
+ const nextPreview = extractLinkPreview(data, nextUrl);
10151
+ if (!isPreviewControlled) {
10152
+ setInternalPreview(nextPreview);
10153
+ }
10154
+ setUrl(nextPreview.url);
10155
+ setSubmittedUrl(nextPreview.url);
10156
+ setStatus("idle");
10157
+ setExpanded(false);
10158
+ onValueChange?.(nextPreview, data);
10159
+ onSubmit?.(data, nextPreview);
10160
+ } catch (submitError) {
10161
+ setStatus("error");
10162
+ setError(
10163
+ submitError instanceof Error ? submitError.message : "Something went wrong."
10164
+ );
10165
+ }
10166
+ }
10167
+ function handleEdit() {
10168
+ if (disabled) return;
10169
+ setUrl(submittedUrl);
10170
+ setStatus("idle");
10171
+ setError(null);
10172
+ setExpanded(true);
10173
+ }
10174
+ function handleClose() {
10175
+ setUrl(submittedUrl);
10176
+ setStatus("idle");
10177
+ setError(null);
10178
+ setExpanded(false);
10179
+ }
10180
+ function handleDelete() {
10181
+ if (disabled) return;
10182
+ if (!isPreviewControlled) {
10183
+ setInternalPreview(null);
10184
+ }
10185
+ setUrl("");
10186
+ setSubmittedUrl("");
10187
+ setStatus("idle");
10188
+ setError(null);
10189
+ setExpanded(false);
10190
+ onValueChange?.(null, null);
10191
+ onClear?.();
10192
+ }
10193
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10194
+ "div",
10195
+ {
10196
+ "data-state": isExpanded ? "editing" : hasPreview ? "filled" : "idle",
10197
+ className: cn(
10198
+ "group/link-input overflow-hidden rounded-xl border bg-card text-card-foreground transition-colors",
10199
+ isExpanded || hasPreview ? "border-foreground/80 shadow-sm" : "border-border hover:border-foreground/30",
10200
+ disabled && "opacity-60",
10201
+ className
10202
+ ),
10203
+ children: [
10204
+ isExpanded ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex w-full flex-col gap-3 px-4 py-3.5", children: [
10205
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-5 items-center justify-center text-muted-foreground [&_svg]:size-5", children: resolvedIcon }),
10206
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 text-sm font-medium leading-5 text-foreground", children: label }),
10207
+ /* @__PURE__ */ jsxRuntime.jsx(
10208
+ "button",
10209
+ {
10210
+ type: "button",
10211
+ onClick: handleClose,
10212
+ "aria-label": "Close link input",
10213
+ className: "absolute right-2.5 top-2.5 flex size-6 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/60",
10214
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-4" })
10215
+ }
10216
+ )
10217
+ ] }) : hasPreview ? /* @__PURE__ */ jsxRuntime.jsx(
10218
+ LinkInputPreview,
10219
+ {
10220
+ preview,
10221
+ onEdit: handleEdit,
10222
+ onDelete: handleDelete,
10223
+ disabled
10224
+ }
10225
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
10226
+ "button",
10227
+ {
10228
+ type: "button",
10229
+ onClick: () => setExpanded(true),
10230
+ "aria-expanded": false,
10231
+ disabled,
10232
+ className: "flex w-full flex-col gap-3 px-4 py-3.5 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring/60 disabled:cursor-not-allowed",
10233
+ children: [
10234
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-5 items-center justify-center text-muted-foreground [&_svg]:size-5", children: resolvedIcon }),
10235
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 text-sm font-medium leading-5 text-foreground", children: label })
10236
+ ]
10237
+ }
10238
+ ),
10239
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border px-4 pb-4 pt-3", children: /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
10240
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
10241
+ /* @__PURE__ */ jsxRuntime.jsx(
10242
+ Input,
10243
+ {
10244
+ ref: inputRef,
10245
+ id: inputId,
10246
+ type: "text",
10247
+ inputMode: "url",
10248
+ autoComplete: "off",
10249
+ spellCheck: false,
10250
+ value: url,
10251
+ disabled: disabled || isLoading,
10252
+ "aria-invalid": status === "error",
10253
+ "aria-describedby": status === "error" ? errorId : void 0,
10254
+ onChange: (event) => {
10255
+ setUrl(event.target.value);
10256
+ if (status === "error") {
10257
+ setStatus("idle");
10258
+ setError(null);
10259
+ }
10260
+ },
10261
+ placeholder,
10262
+ className: cn(
10263
+ "h-10 rounded-lg bg-background pr-[5.25rem] text-sm shadow-sm placeholder:text-muted-foreground/70 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40",
10264
+ status === "error" && "border-destructive/60"
10265
+ )
10266
+ }
10267
+ ),
10268
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-1.5 flex items-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
10269
+ Button,
10270
+ {
10271
+ type: "submit",
10272
+ size: "sm",
10273
+ disabled: !url.trim() || isLoading || disabled,
10274
+ className: "h-7 gap-1 px-2",
10275
+ children: [
10276
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { "aria-hidden": true, className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CornerDownLeft, { "aria-hidden": true, className: "size-3.5" }),
10277
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: isLoading ? "Loading" : "Enter" })
10278
+ ]
10279
+ }
10280
+ ) })
10281
+ ] }),
10282
+ status === "error" && error ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, role: "alert", className: "px-0.5 text-xs text-destructive", children: error }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "px-0.5 text-xs text-muted-foreground", children: [
10283
+ "Press",
10284
+ " ",
10285
+ /* @__PURE__ */ jsxRuntime.jsx("kbd", { className: "rounded border border-border bg-muted px-1 font-sans text-[0.65rem] text-muted-foreground", children: "Enter" }),
10286
+ " ",
10287
+ "to fetch a preview."
10288
+ ] })
10289
+ ] }) })
10290
+ ]
10291
+ }
10292
+ );
10293
+ }
10294
+ function LinkInputPreview({
10295
+ preview,
10296
+ onEdit,
10297
+ onDelete,
10298
+ disabled
10299
+ }) {
10300
+ const [imageError, setImageError] = React4.useState(false);
10301
+ const [faviconError, setFaviconError] = React4.useState(false);
10302
+ const showImage = Boolean(preview.image) && !imageError;
10303
+ const showFavicon = Boolean(preview.favicon) && !faviconError;
10304
+ const title = preview.title || preview.siteName || preview.domain || "Untitled link";
10305
+ React4.useEffect(() => {
10306
+ setImageError(false);
10307
+ setFaviconError(false);
10308
+ }, [preview.favicon, preview.image]);
10309
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-stretch gap-3 p-3", children: [
10310
+ /* @__PURE__ */ jsxRuntime.jsx(
10311
+ "a",
10312
+ {
10313
+ href: preview.url || "#",
10314
+ target: "_blank",
10315
+ rel: "noopener noreferrer",
10316
+ className: "relative flex size-16 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border bg-muted outline-none focus-visible:ring-2 focus-visible:ring-ring/60",
10317
+ children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
10318
+ "img",
10319
+ {
10320
+ src: preview.image,
10321
+ alt: "",
10322
+ crossOrigin: "anonymous",
10323
+ onError: () => setImageError(true),
10324
+ className: "size-full object-cover"
10325
+ }
10326
+ ) : showFavicon ? /* @__PURE__ */ jsxRuntime.jsx(
10327
+ "img",
10328
+ {
10329
+ src: preview.favicon,
10330
+ alt: "",
10331
+ crossOrigin: "anonymous",
10332
+ onError: () => setFaviconError(true),
10333
+ className: "size-7"
10334
+ }
10335
+ ) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Globe, { "aria-hidden": true, className: "size-7 text-muted-foreground" })
10336
+ }
10337
+ ),
10338
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center py-0.5", children: [
10339
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
10340
+ showFavicon && showImage ? /* @__PURE__ */ jsxRuntime.jsx(
10341
+ "img",
10342
+ {
10343
+ src: preview.favicon,
10344
+ alt: "",
10345
+ crossOrigin: "anonymous",
10346
+ onError: () => setFaviconError(true),
10347
+ className: "size-3.5 shrink-0 rounded-sm"
10348
+ }
10349
+ ) : null,
10350
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs font-medium text-muted-foreground", children: preview.domain || preview.siteName })
10351
+ ] }),
10352
+ /* @__PURE__ */ jsxRuntime.jsx(
10353
+ "a",
10354
+ {
10355
+ href: preview.url || "#",
10356
+ target: "_blank",
10357
+ rel: "noopener noreferrer",
10358
+ className: "mt-0.5 line-clamp-1 text-sm font-semibold text-foreground outline-none hover:underline focus-visible:underline",
10359
+ children: title
10360
+ }
10361
+ ),
10362
+ preview.description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 line-clamp-2 text-xs leading-4 text-muted-foreground", children: preview.description }) : null
10363
+ ] }),
10364
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-col items-center gap-1", children: [
10365
+ /* @__PURE__ */ jsxRuntime.jsx(
10366
+ Button,
10367
+ {
10368
+ type: "button",
10369
+ size: "icon",
10370
+ variant: "ghost",
10371
+ onClick: onEdit,
10372
+ disabled,
10373
+ "aria-label": "Edit link",
10374
+ className: "size-7 rounded-full",
10375
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "size-3.5" })
10376
+ }
10377
+ ),
10378
+ /* @__PURE__ */ jsxRuntime.jsx(
10379
+ Button,
10380
+ {
10381
+ type: "button",
10382
+ size: "icon",
10383
+ variant: "ghost",
10384
+ onClick: onDelete,
10385
+ disabled,
10386
+ "aria-label": "Delete link",
10387
+ className: "size-7 rounded-full",
10388
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "size-3.5" })
10389
+ }
10390
+ )
10391
+ ] })
10392
+ ] });
10393
+ }
10394
+ function LinkInputGroup({
10395
+ options,
10396
+ onSubmit,
10397
+ onValueChange,
10398
+ onClear,
10399
+ className
10400
+ }) {
10401
+ const [openId, setOpenId] = React4.useState(null);
10402
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col gap-3", className), children: options.map(({ id, ...option }) => /* @__PURE__ */ jsxRuntime.jsx(
10403
+ LinkInput,
10404
+ {
10405
+ ...option,
10406
+ expanded: openId === id,
10407
+ onExpandedChange: (next) => setOpenId((currentId) => {
10408
+ if (next) return id;
10409
+ return currentId === id ? null : currentId;
10410
+ }),
10411
+ onSubmit: (data, preview) => {
10412
+ setOpenId(null);
10413
+ onSubmit?.(id, data, preview);
10414
+ },
10415
+ onValueChange: (preview, data) => {
10416
+ onValueChange?.(id, preview, data);
10417
+ },
10418
+ onClear: () => onClear?.(id)
10419
+ },
10420
+ id
10421
+ )) });
10422
+ }
10423
+ var getDimensionStyle = (width, height) => {
10424
+ const style = {};
10425
+ if (width) style.width = typeof width === "number" ? `${width}px` : width;
10426
+ if (height) style.height = typeof height === "number" ? `${height}px` : height;
10427
+ return style;
10428
+ };
10429
+ var getThumbnailSizing = (thumbnailHeight) => ({
10430
+ heightClass: typeof thumbnailHeight === "number" ? "" : thumbnailHeight,
10431
+ heightStyle: typeof thumbnailHeight === "number" ? { height: `${thumbnailHeight}px` } : void 0
10432
+ });
10433
+ function MediaFallback() {
10434
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full min-h-28 w-full items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ImageIcon, { className: "size-7", "aria-hidden": "true" }) });
10435
+ }
10436
+ function MediaOptionItem({
10437
+ option,
10438
+ isChecked,
10439
+ onToggle,
10440
+ thumbnailHeight
10441
+ }) {
10442
+ const {
10443
+ value,
10444
+ label,
10445
+ subtitle,
10446
+ mediaUrl,
10447
+ mediaType,
10448
+ thumbnailUrl,
10449
+ width,
10450
+ height,
10451
+ disabled
10452
+ } = option;
10453
+ const videoRef = React4.useRef(null);
10454
+ const [playing, setPlaying] = React4.useState(false);
10455
+ const [mediaError, setMediaError] = React4.useState(!mediaUrl);
10456
+ const sizeStyle = getDimensionStyle(width, height);
10457
+ const { heightClass, heightStyle } = getThumbnailSizing(thumbnailHeight);
10458
+ const handleMouseEnter = () => {
10459
+ if (mediaType === "video" && videoRef.current && !disabled) {
10460
+ videoRef.current.play().then(() => setPlaying(true)).catch(() => {
10461
+ });
10462
+ }
10463
+ };
10464
+ const handleMouseLeave = () => {
10465
+ if (mediaType === "video" && videoRef.current) {
10466
+ videoRef.current.pause();
10467
+ setPlaying(false);
10468
+ }
10469
+ };
10470
+ const handleVideoClick = (e) => {
10471
+ e.stopPropagation();
10472
+ if (mediaType !== "video" || !videoRef.current || disabled) return;
10473
+ if (playing) {
10474
+ videoRef.current.pause();
10475
+ setPlaying(false);
10476
+ return;
10477
+ }
10478
+ videoRef.current.play().then(() => setPlaying(true)).catch(() => {
10479
+ });
10480
+ };
10481
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10482
+ "label",
10483
+ {
10484
+ className: cn(
10485
+ "group block h-full",
10486
+ disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer"
10487
+ ),
10488
+ style: sizeStyle,
10489
+ children: [
10490
+ /* @__PURE__ */ jsxRuntime.jsx(
10491
+ "input",
10492
+ {
10493
+ type: "checkbox",
10494
+ className: "peer sr-only",
10495
+ checked: isChecked,
10496
+ disabled,
10497
+ onChange: () => onToggle(value)
10498
+ }
10499
+ ),
10500
+ /* @__PURE__ */ jsxRuntime.jsxs(
10501
+ "span",
10502
+ {
10503
+ className: cn(
10504
+ "relative flex h-full flex-col overflow-hidden rounded-lg border bg-card text-card-foreground shadow-xs transition-all duration-200 ease-in-out",
10505
+ "border-input hover:border-border hover:shadow-sm",
10506
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background",
10507
+ isChecked && "border-primary shadow-sm ring-1 ring-primary/30 hover:border-primary",
10508
+ disabled && "pointer-events-none"
10509
+ ),
10510
+ children: [
10511
+ /* @__PURE__ */ jsxRuntime.jsxs(
10512
+ "span",
10513
+ {
10514
+ className: cn("relative block overflow-hidden bg-muted", heightClass),
10515
+ style: heightStyle,
10516
+ onMouseEnter: handleMouseEnter,
10517
+ onMouseLeave: handleMouseLeave,
10518
+ children: [
10519
+ mediaError ? /* @__PURE__ */ jsxRuntime.jsx(MediaFallback, {}) : mediaType === "image" ? /* @__PURE__ */ jsxRuntime.jsx(
10520
+ "img",
10521
+ {
10522
+ src: mediaUrl,
10523
+ alt: label,
10524
+ className: "h-full w-full object-cover transition-transform duration-300 ease-out group-hover:scale-[1.015]",
10525
+ onError: () => setMediaError(true)
10526
+ }
10527
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10528
+ /* @__PURE__ */ jsxRuntime.jsx(
10529
+ "video",
10530
+ {
10531
+ ref: videoRef,
10532
+ src: mediaUrl,
10533
+ poster: thumbnailUrl,
10534
+ muted: true,
10535
+ loop: true,
10536
+ playsInline: true,
10537
+ preload: "metadata",
10538
+ onClick: handleVideoClick,
10539
+ onError: () => setMediaError(true),
10540
+ className: cn(
10541
+ "h-full w-full object-cover transition duration-300 ease-out group-hover:scale-[1.015]",
10542
+ playing ? "grayscale-0" : "grayscale"
10543
+ )
10544
+ }
10545
+ ),
10546
+ /* @__PURE__ */ jsxRuntime.jsx(
10547
+ "span",
10548
+ {
10549
+ className: cn(
10550
+ "absolute bottom-2 left-2 flex size-7 items-center justify-center rounded-full bg-background/85 text-foreground shadow-xs backdrop-blur",
10551
+ playing && "opacity-0"
10552
+ ),
10553
+ "aria-hidden": "true",
10554
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "ml-0.5 size-3.5 fill-current" })
10555
+ }
10556
+ )
10557
+ ] }),
10558
+ /* @__PURE__ */ jsxRuntime.jsx(
10559
+ "span",
10560
+ {
10561
+ className: cn(
10562
+ "absolute right-3 top-3 flex size-6 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-xs transition-all duration-200",
10563
+ isChecked ? "scale-100 opacity-100 ring-2 ring-white ring-offset-2" : "scale-75 opacity-0"
10564
+ ),
10565
+ "aria-hidden": "true",
10566
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "size-3.5 stroke-[2.5]" })
10567
+ }
10568
+ )
10569
+ ]
10570
+ }
10571
+ ),
10572
+ /* @__PURE__ */ jsxRuntime.jsxs(
10573
+ "span",
10574
+ {
10575
+ className: cn(
10576
+ "flex min-h-[82px] flex-col gap-1 border-t border-border bg-card px-3 py-3 text-card-foreground",
10577
+ isChecked && "bg-primary text-primary-foreground"
10578
+ ),
10579
+ children: [
10580
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-1 text-sm font-semibold leading-5", children: label }),
10581
+ subtitle && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-sm leading-5", children: subtitle })
10582
+ ]
10583
+ }
10584
+ )
10585
+ ]
10586
+ }
10587
+ )
10588
+ ]
10589
+ }
10590
+ );
10591
+ }
10592
+ function MediaCheckboxes({
10593
+ options,
10594
+ selectedValues,
10595
+ defaultSelectedValues,
10596
+ onSelect,
10597
+ minSelection = 0,
10598
+ maxSelection,
10599
+ className,
10600
+ thumbnailHeight = "h-48"
10601
+ }) {
10602
+ const isControlled = Array.isArray(selectedValues);
10603
+ const [internal, setInternal] = React4.useState(
10604
+ defaultSelectedValues ?? []
10605
+ );
10606
+ const current = isControlled ? selectedValues : internal;
10607
+ const max = maxSelection ?? options.length;
10608
+ const toggleOption = React4.useCallback(
10609
+ (value) => {
10610
+ const isSelected = current.includes(value);
10611
+ let updated;
10612
+ if (isSelected) {
10613
+ updated = current.filter((v) => v !== value);
10614
+ if (updated.length < minSelection) return;
10615
+ } else if (max === 1) {
10616
+ updated = [value];
10617
+ } else {
10618
+ if (current.length >= max) return;
10619
+ updated = [...current, value];
10620
+ }
10621
+ if (!isControlled) setInternal(updated);
10622
+ onSelect?.(updated);
10623
+ },
10624
+ [current, isControlled, max, minSelection, onSelect]
10625
+ );
10626
+ return /* @__PURE__ */ jsxRuntime.jsx(
10627
+ "fieldset",
10628
+ {
10629
+ className: cn(
10630
+ "grid w-full grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4",
10631
+ className
10632
+ ),
10633
+ children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
10634
+ MediaOptionItem,
10635
+ {
10636
+ option,
10637
+ isChecked: current.includes(option.value),
10638
+ onToggle: toggleOption,
10639
+ thumbnailHeight
10640
+ },
10641
+ option.value
10642
+ ))
10643
+ }
10644
+ );
10645
+ }
10646
+ function renderTileIcon(item) {
10647
+ if (item.icon != null) {
10648
+ if (React4.isValidElement(item.icon)) {
10649
+ return React4.cloneElement(item.icon, {
10650
+ className: cn(item.icon.props.className, "size-5 shrink-0")
10651
+ });
10652
+ }
10653
+ return item.icon;
10654
+ }
10655
+ return resolveIconName(item.iconName);
10656
+ }
10657
+ function IconCheckboxes({
10658
+ options,
10659
+ selectedValues,
10660
+ defaultSelectedValues,
10661
+ onSelect,
10662
+ minSelection = 0,
10663
+ maxSelection,
10664
+ className
10665
+ }) {
10666
+ const isControlled = Array.isArray(selectedValues);
10667
+ const [internal, setInternal] = React4.useState(
10668
+ defaultSelectedValues ?? []
10669
+ );
10670
+ const current = isControlled ? selectedValues : internal;
10671
+ const max = maxSelection ?? options.length;
10672
+ const commit = (updated) => {
10673
+ if (!isControlled) setInternal(updated);
10674
+ onSelect?.(updated);
10675
+ };
10676
+ const toggleOption = (value) => {
10677
+ const isSelected = current.includes(value);
10678
+ let updated;
10679
+ if (isSelected) {
10680
+ updated = current.filter((v) => v !== value);
10681
+ if (updated.length < minSelection) return;
10682
+ } else if (max === 1) {
10683
+ updated = [value];
10684
+ } else {
10685
+ if (current.length >= max) return;
10686
+ updated = [...current, value];
10687
+ }
10688
+ commit(updated);
10689
+ };
10690
+ return /* @__PURE__ */ jsxRuntime.jsx(
10691
+ "fieldset",
10692
+ {
10693
+ className: cn(
10694
+ "grid w-full max-w-sm grid-cols-[repeat(auto-fit,minmax(7rem,1fr))] gap-3",
10695
+ className
10696
+ ),
10697
+ children: options.map((item) => {
10698
+ const { value, label, disabled } = item;
10699
+ const isChecked = current.includes(value);
10700
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10701
+ "label",
10702
+ {
10703
+ className: cn(
10704
+ "block",
10705
+ disabled ? "cursor-not-allowed opacity-60" : "cursor-pointer"
10706
+ ),
10707
+ children: [
10708
+ /* @__PURE__ */ jsxRuntime.jsx(
10709
+ "input",
10710
+ {
10711
+ type: "checkbox",
10712
+ className: "peer sr-only",
10713
+ checked: isChecked,
10714
+ disabled,
10715
+ onChange: () => toggleOption(value)
10716
+ }
10717
+ ),
10718
+ /* @__PURE__ */ jsxRuntime.jsxs(
10719
+ "span",
10720
+ {
10721
+ className: cn(
10722
+ "relative flex min-h-[88px] flex-col items-start justify-center rounded-lg border bg-card px-4 py-3 text-left text-muted-foreground transition-all duration-200 ease-in-out",
10723
+ "border-input hover:border-border hover:bg-accent/40",
10724
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background",
10725
+ isChecked && "border-primary bg-primary/5 text-primary shadow-xs ring-1 ring-primary/20 hover:border-primary hover:bg-primary/5",
10726
+ disabled && "pointer-events-none"
10727
+ ),
10728
+ children: [
10729
+ /* @__PURE__ */ jsxRuntime.jsx(
10730
+ "span",
10731
+ {
10732
+ "aria-hidden": "true",
10733
+ className: "mb-3 flex size-5 items-center justify-center",
10734
+ children: renderTileIcon(item)
10735
+ }
10736
+ ),
10737
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-full break-words text-sm font-medium leading-tight transition-colors duration-200 ease-in-out", children: label }),
10738
+ /* @__PURE__ */ jsxRuntime.jsx(
10739
+ lucideReact.CircleCheck,
10740
+ {
10741
+ "aria-hidden": "true",
10742
+ className: cn(
10743
+ "absolute right-2 top-2 size-5 rounded-full fill-primary text-primary-foreground transition-all duration-200 ease-in-out",
10744
+ isChecked ? "scale-100 opacity-100" : "scale-75 opacity-0"
10745
+ )
10746
+ }
10747
+ )
10748
+ ]
10749
+ }
10750
+ )
10751
+ ]
10752
+ },
10753
+ value
10754
+ );
10755
+ })
10756
+ }
10757
+ );
10758
+ }
9952
10759
  var ARTIFACT_REGISTRY = {
9953
10760
  chart: ({ payload, className }) => payload.chart ? /* @__PURE__ */ jsxRuntime.jsx(ChartContainer, { data: payload.chart, className }) : null,
9954
10761
  metrics: ({ payload, className }) => payload.metrics ? /* @__PURE__ */ jsxRuntime.jsx(MetricsGrid, { metrics: payload.metrics, className }) : null,
@@ -9966,7 +10773,10 @@ var ARTIFACT_REGISTRY = {
9966
10773
  "deep-research-progress": ({ payload, className }) => payload.deepResearchProgress ? /* @__PURE__ */ jsxRuntime.jsx(DeepResearchProgress, { ...payload.deepResearchProgress, className }) : null,
9967
10774
  tracker: ({ payload, className }) => payload.tracker ? /* @__PURE__ */ jsxRuntime.jsx(Tracker, { ...payload.tracker, className }) : null,
9968
10775
  "built-in-questions": ({ payload, className }) => payload.builtInQuestions ? /* @__PURE__ */ jsxRuntime.jsx(BuiltInQuestions, { ...payload.builtInQuestions, className }) : null,
9969
- "audio-player": ({ payload, className }) => payload.audioPlayer ? /* @__PURE__ */ jsxRuntime.jsx(AudioPlayer, { ...payload.audioPlayer, className }) : null
10776
+ "audio-player": ({ payload, className }) => payload.audioPlayer ? /* @__PURE__ */ jsxRuntime.jsx(AudioPlayer, { ...payload.audioPlayer, className }) : null,
10777
+ "link-input": ({ payload, className }) => payload.linkInput ? /* @__PURE__ */ jsxRuntime.jsx(LinkInput, { ...payload.linkInput, className }) : null,
10778
+ "media-checkboxes": ({ payload, className }) => payload.mediaCheckboxes ? /* @__PURE__ */ jsxRuntime.jsx(MediaCheckboxes, { ...payload.mediaCheckboxes, className }) : null,
10779
+ "icon-checkboxes": ({ payload, className }) => payload.iconCheckboxes ? /* @__PURE__ */ jsxRuntime.jsx(IconCheckboxes, { ...payload.iconCheckboxes, className }) : null
9970
10780
  };
9971
10781
  function DataPayloadView({ payload, className }) {
9972
10782
  const render = ARTIFACT_REGISTRY[payload.type];
@@ -11425,6 +12235,7 @@ exports.ConfirmationPanel = ConfirmationPanel;
11425
12235
  exports.ControlGrid = ControlGrid;
11426
12236
  exports.ConversationAnalytics = ConversationAnalytics;
11427
12237
  exports.ConversationArtifact = ConversationArtifact;
12238
+ exports.DEFAULT_OPEN_GRAPH_ENDPOINT = DEFAULT_OPEN_GRAPH_ENDPOINT;
11428
12239
  exports.DataPayloadView = DataPayloadView;
11429
12240
  exports.DataTable = DataTable;
11430
12241
  exports.DeepResearchProgress = DeepResearchProgress;
@@ -11435,13 +12246,17 @@ exports.FloatingWidget = FloatingWidget;
11435
12246
  exports.FullBleedSurface = FullBleedSurface;
11436
12247
  exports.FullscreenDashboard = FullscreenDashboard;
11437
12248
  exports.GuidedLessonFlow = GuidedLessonFlow;
12249
+ exports.IconCheckboxes = IconCheckboxes;
11438
12250
  exports.ImageGenerator = ImageGenerator;
11439
12251
  exports.InlineSuggestionsInput = InlineSuggestionsInput;
11440
12252
  exports.Input = Input;
11441
12253
  exports.KpiCardWithChart = KpiCardWithChart;
11442
12254
  exports.KpiCardWithSparklines = KpiCardWithSparklines;
12255
+ exports.LinkInput = LinkInput;
12256
+ exports.LinkInputGroup = LinkInputGroup;
11443
12257
  exports.ListingFeed = ListingFeed;
11444
12258
  exports.LocationsRevenueCard = LocationsRevenueCard;
12259
+ exports.MediaCheckboxes = MediaCheckboxes;
11445
12260
  exports.MediaEditorCanvas = MediaEditorCanvas;
11446
12261
  exports.MediaGallery = MediaGallery;
11447
12262
  exports.MessageActions = MessageActions;
@@ -11509,6 +12324,8 @@ exports.copyToClipboard = copyToClipboard;
11509
12324
  exports.createMockBackend = createMockBackend;
11510
12325
  exports.debounce = debounce;
11511
12326
  exports.delay = delay;
12327
+ exports.extractLinkPreview = extractLinkPreview;
12328
+ exports.fetchOpenGraphPreview = fetchOpenGraphPreview;
11512
12329
  exports.findComponentsByCapability = findComponentsByCapability;
11513
12330
  exports.findComponentsByCategory = findComponentsByCategory;
11514
12331
  exports.findComponentsBySurface = findComponentsBySurface;