@limboai/react 0.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.
Files changed (60) hide show
  1. package/README.md +134 -0
  2. package/dist/icons/CRLogo.d.ts +12 -0
  3. package/dist/icons/LinkedInIcon.d.ts +13 -0
  4. package/dist/icons/index.d.ts +4 -0
  5. package/dist/index.cjs +2169 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.d.ts +11 -0
  8. package/dist/index.mjs +2147 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/dist/shared/hooks/index.d.ts +2 -0
  11. package/dist/shared/hooks/useTooltipVisibility.d.ts +32 -0
  12. package/dist/shared/index.d.ts +2 -0
  13. package/dist/types/index.d.ts +59 -0
  14. package/dist/types/media.d.ts +56 -0
  15. package/dist/ui/core/Slot.d.ts +33 -0
  16. package/dist/ui/core/credential/CredentialBadge.d.ts +63 -0
  17. package/dist/ui/core/credential/CredentialOverlayContent.d.ts +23 -0
  18. package/dist/ui/core/credential/CredentialOverlayRoot.d.ts +35 -0
  19. package/dist/ui/core/credential/CredentialOverlayTrigger.d.ts +23 -0
  20. package/dist/ui/core/credential/context/CredentialBadgeContext.d.ts +8 -0
  21. package/dist/ui/core/credential/context/CredentialOverlayContext.d.ts +21 -0
  22. package/dist/ui/core/credential/context/index.d.ts +2 -0
  23. package/dist/ui/core/credential/hooks/index.d.ts +2 -0
  24. package/dist/ui/core/credential/hooks/useCredentialBadgeContext.d.ts +15 -0
  25. package/dist/ui/core/credential/hooks/useCredentialOverlayContext.d.ts +23 -0
  26. package/dist/ui/core/credential/index.d.ts +36 -0
  27. package/dist/ui/core/index.d.ts +2 -0
  28. package/dist/ui/core/passport/PassportActions.d.ts +27 -0
  29. package/dist/ui/core/passport/PassportCopyButton.d.ts +38 -0
  30. package/dist/ui/core/passport/PassportField.d.ts +39 -0
  31. package/dist/ui/core/passport/PassportFooter.d.ts +20 -0
  32. package/dist/ui/core/passport/PassportHeader.d.ts +19 -0
  33. package/dist/ui/core/passport/PassportIdentities.d.ts +35 -0
  34. package/dist/ui/core/passport/PassportLogo.d.ts +25 -0
  35. package/dist/ui/core/passport/PassportRoot.d.ts +24 -0
  36. package/dist/ui/core/passport/PassportSigners.d.ts +28 -0
  37. package/dist/ui/core/passport/PassportTitle.d.ts +30 -0
  38. package/dist/ui/core/passport/context/PassportContext.d.ts +12 -0
  39. package/dist/ui/core/passport/context/index.d.ts +1 -0
  40. package/dist/ui/core/passport/hooks/index.d.ts +2 -0
  41. package/dist/ui/core/passport/hooks/usePassportContext.d.ts +20 -0
  42. package/dist/ui/core/passport/hooks/usePassportData.d.ts +22 -0
  43. package/dist/ui/core/passport/index.d.ts +49 -0
  44. package/dist/ui/default/LimboBadge.d.ts +22 -0
  45. package/dist/ui/default/LimboImage.d.ts +66 -0
  46. package/dist/ui/default/LimboPassport.d.ts +30 -0
  47. package/dist/ui/default/LimboVideo.d.ts +64 -0
  48. package/dist/ui/default/constants.d.ts +19 -0
  49. package/dist/ui/default/index.d.ts +10 -0
  50. package/dist/ui/index.d.ts +3 -0
  51. package/dist/ui/media/MediaImage.d.ts +87 -0
  52. package/dist/ui/media/MediaVideo.d.ts +85 -0
  53. package/dist/ui/media/context/MediaContext.d.ts +19 -0
  54. package/dist/ui/media/context/index.d.ts +1 -0
  55. package/dist/ui/media/hooks/index.d.ts +3 -0
  56. package/dist/ui/media/hooks/useHeicConversion.d.ts +32 -0
  57. package/dist/ui/media/hooks/useMediaContext.d.ts +13 -0
  58. package/dist/ui/media/hooks/useMediaOverlay.d.ts +37 -0
  59. package/dist/ui/media/index.d.ts +5 -0
  60. package/package.json +71 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,2147 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { Children, cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useDebugValue, useEffect, useId, useMemo, useRef, useState, useTransition } from "react";
4
+
5
+ //#region src/icons/CRLogo.tsx
6
+ const CR_LOGO_BASE64 = "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";
7
+ /**
8
+ * Content Credentials (CR) Logo
9
+ *
10
+ * Based on the Content Authenticity Initiative logo
11
+ */
12
+ const CRLogo = ({ size = 20, className, style }) => {
13
+ return /* @__PURE__ */ jsx("img", {
14
+ src: `data:image/png;base64,${CR_LOGO_BASE64}`,
15
+ width: size,
16
+ height: size,
17
+ className,
18
+ style,
19
+ alt: "Content Credentials"
20
+ });
21
+ };
22
+
23
+ //#endregion
24
+ //#region src/icons/LinkedInIcon.tsx
25
+ /**
26
+ * LinkedIn Icon as inline SVG
27
+ *
28
+ * Used for LinkedIn identity verification links
29
+ */
30
+ const LinkedInIcon = ({ size = 14, color = "#0077B5", className, style }) => {
31
+ return /* @__PURE__ */ jsx("svg", {
32
+ width: size,
33
+ height: size,
34
+ viewBox: "0 0 24 24",
35
+ fill: color,
36
+ className,
37
+ style,
38
+ "aria-hidden": "true",
39
+ children: /* @__PURE__ */ jsx("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" })
40
+ });
41
+ };
42
+
43
+ //#endregion
44
+ //#region src/shared/hooks/useTooltipVisibility.ts
45
+ /**
46
+ * Hook for managing tooltip visibility with delayed hide
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * const { isVisible, show, hide, toggle } = useTooltipVisibility({
51
+ * hasCredentials: validation?.has_credentials ?? false,
52
+ * hideDelay: 300,
53
+ * });
54
+ * ```
55
+ */
56
+ function useTooltipVisibility({ hasCredentials, initialVisible = false, hideDelay = 300 }) {
57
+ const [isVisible, setIsVisible] = useState(initialVisible);
58
+ const timeoutRef = useRef(null);
59
+ const clearPendingHide = useCallback(() => {
60
+ if (timeoutRef.current !== null) {
61
+ clearTimeout(timeoutRef.current);
62
+ timeoutRef.current = null;
63
+ }
64
+ }, []);
65
+ useEffect(() => clearPendingHide, [clearPendingHide]);
66
+ const show = useCallback(() => {
67
+ clearPendingHide();
68
+ if (hasCredentials) setIsVisible(true);
69
+ }, [hasCredentials, clearPendingHide]);
70
+ const hide = useCallback(() => {
71
+ clearPendingHide();
72
+ timeoutRef.current = setTimeout(() => setIsVisible(false), hideDelay);
73
+ }, [hideDelay, clearPendingHide]);
74
+ const toggle = useCallback(() => {
75
+ clearPendingHide();
76
+ if (hasCredentials) setIsVisible((v) => !v);
77
+ }, [hasCredentials, clearPendingHide]);
78
+ const setVisible = useCallback((visible) => {
79
+ clearPendingHide();
80
+ if (!visible || hasCredentials) setIsVisible(visible);
81
+ }, [hasCredentials, clearPendingHide]);
82
+ useDebugValue({
83
+ isVisible,
84
+ hasCredentials
85
+ });
86
+ return {
87
+ isVisible,
88
+ show,
89
+ hide,
90
+ toggle,
91
+ setVisible
92
+ };
93
+ }
94
+
95
+ //#endregion
96
+ //#region src/types/media.ts
97
+ /**
98
+ * Field names available in passport components
99
+ */
100
+ const PassportFieldName = {
101
+ Issuer: "issuer",
102
+ Date: "date"
103
+ };
104
+ /**
105
+ * Passport display variants
106
+ */
107
+ const PassportVariant = {
108
+ Basic: "basic",
109
+ Full: "full"
110
+ };
111
+
112
+ //#endregion
113
+ //#region src/types/index.ts
114
+ /**
115
+ * Validation status enum with const assertion and satisfies for type safety
116
+ */
117
+ const ValidationStatus = {
118
+ Idle: "idle",
119
+ Error: "error",
120
+ Success: "success",
121
+ Loading: "loading"
122
+ };
123
+
124
+ //#endregion
125
+ //#region src/ui/core/credential/context/CredentialBadgeContext.tsx
126
+ const CredentialBadgeContext = createContext(null);
127
+
128
+ //#endregion
129
+ //#region src/ui/core/credential/hooks/useCredentialBadgeContext.ts
130
+ /**
131
+ * Access the CredentialBadge context
132
+ *
133
+ * Must be used within a CredentialBadge.Root component
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * function MyBadgeContent() {
138
+ * const { status, hasCredentials } = useCredentialBadgeContext();
139
+ * return <div>{status}</div>;
140
+ * }
141
+ * ```
142
+ */
143
+ function useCredentialBadgeContext() {
144
+ const context = useContext(CredentialBadgeContext);
145
+ if (!context) throw new Error("useCredentialBadgeContext must be used within a CredentialBadge.Root");
146
+ useDebugValue({
147
+ status: context.status,
148
+ hasCredentials: context.hasCredentials
149
+ });
150
+ return context;
151
+ }
152
+
153
+ //#endregion
154
+ //#region src/ui/core/credential/context/CredentialOverlayContext.tsx
155
+ const CredentialOverlayContext = createContext(null);
156
+
157
+ //#endregion
158
+ //#region src/ui/core/credential/hooks/useCredentialOverlayContext.ts
159
+ /**
160
+ * Access the CredentialOverlay context
161
+ *
162
+ * Must be used within a CredentialOverlay.Root component
163
+ *
164
+ * @example
165
+ * ```tsx
166
+ * function MyTrigger() {
167
+ * const { show, hide, toggle, isVisible } = useCredentialOverlayContext();
168
+ * return (
169
+ * <button
170
+ * onMouseEnter={show}
171
+ * onMouseLeave={hide}
172
+ * onClick={toggle}
173
+ * >
174
+ * {isVisible ? "Hide" : "Show"}
175
+ * </button>
176
+ * );
177
+ * }
178
+ * ```
179
+ */
180
+ function useCredentialOverlayContext() {
181
+ const context = useContext(CredentialOverlayContext);
182
+ if (!context) throw new Error("useCredentialOverlayContext must be used within a CredentialOverlay.Root");
183
+ useDebugValue({
184
+ status: context.status,
185
+ isVisible: context.isVisible
186
+ });
187
+ return context;
188
+ }
189
+
190
+ //#endregion
191
+ //#region src/ui/core/credential/CredentialBadge.tsx
192
+ /**
193
+ * CredentialBadge.Root - Provider wrapper for the CredentialBadge compound component
194
+ *
195
+ * Provides context with status and hasCredentials to all child components.
196
+ * Children are slot components that render based on the current state.
197
+ *
198
+ * @example
199
+ * ```tsx
200
+ * <CredentialBadge.Root status={status} hasCredentials={hasCredentials}>
201
+ * <CredentialBadge.Loading>
202
+ * <Spinner />
203
+ * </CredentialBadge.Loading>
204
+ *
205
+ * <CredentialBadge.Verified>
206
+ * <CheckIcon className="text-green-500" />
207
+ * </CredentialBadge.Verified>
208
+ *
209
+ * <CredentialBadge.NoCredentials>
210
+ * <span>No credentials</span>
211
+ * </CredentialBadge.NoCredentials>
212
+ *
213
+ * <CredentialBadge.Error>
214
+ * <span>Error</span>
215
+ * </CredentialBadge.Error>
216
+ * </CredentialBadge.Root>
217
+ * ```
218
+ */
219
+ const CredentialBadgeRoot = forwardRef(({ status, hasCredentials, children, ...props }, ref) => {
220
+ const contextValue = useMemo(() => ({
221
+ status,
222
+ hasCredentials
223
+ }), [status, hasCredentials]);
224
+ return /* @__PURE__ */ jsx(CredentialBadgeContext.Provider, {
225
+ value: contextValue,
226
+ children: /* @__PURE__ */ jsx("div", {
227
+ ref,
228
+ ...props,
229
+ children
230
+ })
231
+ });
232
+ });
233
+ CredentialBadgeRoot.displayName = "CredentialBadge.Root";
234
+ function createStatusComponent(displayName, shouldRender) {
235
+ const Component = ({ children }) => {
236
+ const { status, hasCredentials } = useCredentialBadgeContext();
237
+ if (!shouldRender(status, hasCredentials)) return null;
238
+ return /* @__PURE__ */ jsx(Fragment, { children });
239
+ };
240
+ Component.displayName = displayName;
241
+ return Component;
242
+ }
243
+ /**
244
+ * CredentialBadge.Loading - Renders children only when status is "loading"
245
+ */
246
+ const CredentialBadgeLoading = createStatusComponent("CredentialBadge.Loading", (status) => status === ValidationStatus.Loading);
247
+ /**
248
+ * CredentialBadge.Error - Renders children only when status is "error"
249
+ */
250
+ const CredentialBadgeError = createStatusComponent("CredentialBadge.Error", (status) => status === ValidationStatus.Error);
251
+ /**
252
+ * CredentialBadge.Verified - Renders children only when status is "success" and has credentials
253
+ */
254
+ const CredentialBadgeVerified = createStatusComponent("CredentialBadge.Verified", (status, hasCredentials) => status === ValidationStatus.Success && hasCredentials);
255
+ /**
256
+ * CredentialBadge.Idle - Renders children only when status is "idle"
257
+ */
258
+ const CredentialBadgeIdle = createStatusComponent("CredentialBadge.Idle", (status) => status === ValidationStatus.Idle);
259
+ /**
260
+ * CredentialBadge.NoCredentials - Renders children only when status is "success" but no credentials
261
+ */
262
+ const CredentialBadgeNoCredentials = createStatusComponent("CredentialBadge.NoCredentials", (status, hasCredentials) => status === ValidationStatus.Success && !hasCredentials);
263
+ /**
264
+ * CredentialBadge - Headless compound component for displaying credential status
265
+ *
266
+ * @example
267
+ * ```tsx
268
+ * import { CredentialBadge } from "@limboai/react";
269
+ *
270
+ * <CredentialBadge.Root status={status} hasCredentials={hasCredentials}>
271
+ * <CredentialBadge.Loading>
272
+ * <div className="animate-pulse bg-blue-500 rounded-full p-2">
273
+ * <Spinner />
274
+ * </div>
275
+ * </CredentialBadge.Loading>
276
+ *
277
+ * <CredentialBadge.Verified>
278
+ * <button className="bg-green-500 rounded-full p-2">
279
+ * <CheckIcon />
280
+ * </button>
281
+ * </CredentialBadge.Verified>
282
+ *
283
+ * <CredentialBadge.NoCredentials>
284
+ * <span className="bg-red-500 text-white text-xs px-2 py-1 rounded">
285
+ * No credentials
286
+ * </span>
287
+ * </CredentialBadge.NoCredentials>
288
+ *
289
+ * <CredentialBadge.Error>
290
+ * <span className="bg-red-500 text-white text-xs px-2 py-1 rounded">
291
+ * Error
292
+ * </span>
293
+ * </CredentialBadge.Error>
294
+ * </CredentialBadge.Root>
295
+ * ```
296
+ */
297
+ const CredentialBadge = {
298
+ Root: CredentialBadgeRoot,
299
+ Loading: CredentialBadgeLoading,
300
+ Verified: CredentialBadgeVerified,
301
+ NoCredentials: CredentialBadgeNoCredentials,
302
+ Error: CredentialBadgeError,
303
+ Idle: CredentialBadgeIdle
304
+ };
305
+
306
+ //#endregion
307
+ //#region src/ui/core/credential/CredentialOverlayContent.tsx
308
+ /**
309
+ * CredentialOverlay.Content - Content that appears when the overlay is visible
310
+ *
311
+ * Only renders when the overlay is visible and credentials exist.
312
+ * Use forceMount to always render (useful for CSS transitions).
313
+ *
314
+ * @example
315
+ * ```tsx
316
+ * <CredentialOverlay.Content className="absolute top-12 right-0 bg-white shadow-lg rounded-lg p-4">
317
+ * <Passport.Root validation={validation}>
318
+ * ...
319
+ * </Passport.Root>
320
+ * </CredentialOverlay.Content>
321
+ * ```
322
+ */
323
+ const CredentialOverlayContent = forwardRef(({ children, forceMount, onMouseLeave, ...props }, ref) => {
324
+ const { isVisible, hasCredentials, validation, hide } = useCredentialOverlayContext();
325
+ if (!forceMount && (!isVisible || !hasCredentials || !validation)) return null;
326
+ const handleMouseLeave = (e) => {
327
+ hide();
328
+ onMouseLeave?.(e);
329
+ };
330
+ return /* @__PURE__ */ jsx("div", {
331
+ ref,
332
+ role: "tooltip",
333
+ tabIndex: -1,
334
+ "data-state": isVisible ? "open" : "closed",
335
+ onMouseLeave: handleMouseLeave,
336
+ ...props,
337
+ children
338
+ });
339
+ });
340
+ CredentialOverlayContent.displayName = "CredentialOverlay.Content";
341
+
342
+ //#endregion
343
+ //#region src/ui/core/credential/CredentialOverlayRoot.tsx
344
+ /**
345
+ * CredentialOverlay.Root - Provider wrapper for the CredentialOverlay compound component
346
+ *
347
+ * Provides context with validation data and visibility state to all child components.
348
+ * Manages tooltip visibility with configurable hide delay.
349
+ *
350
+ * @example
351
+ * ```tsx
352
+ * <CredentialOverlay.Root validation={validation} status="success">
353
+ * <CredentialOverlay.Trigger className="absolute top-2 right-2">
354
+ * <button className="bg-green-500 p-2 rounded-full">✓</button>
355
+ * </CredentialOverlay.Trigger>
356
+ *
357
+ * <CredentialOverlay.Content className="absolute top-12 right-0 bg-white shadow-lg rounded-lg p-4">
358
+ * <Passport.Root validation={validation}>
359
+ * ...
360
+ * </Passport.Root>
361
+ * </CredentialOverlay.Content>
362
+ * </CredentialOverlay.Root>
363
+ * ```
364
+ */
365
+ const CredentialOverlayRoot = forwardRef(({ validation, status = "idle", hideDelay = 300, children, ...props }, ref) => {
366
+ const overlayId = useId();
367
+ const hasCredentials = validation?.has_credentials ?? false;
368
+ const { isVisible, show, hide, toggle } = useTooltipVisibility({
369
+ hasCredentials,
370
+ hideDelay
371
+ });
372
+ const contextValue = useMemo(() => ({
373
+ validation,
374
+ status,
375
+ hasCredentials,
376
+ isVisible,
377
+ show,
378
+ hide,
379
+ toggle,
380
+ overlayId
381
+ }), [
382
+ validation,
383
+ status,
384
+ hasCredentials,
385
+ isVisible,
386
+ show,
387
+ hide,
388
+ toggle,
389
+ overlayId
390
+ ]);
391
+ return /* @__PURE__ */ jsx(CredentialOverlayContext.Provider, {
392
+ value: contextValue,
393
+ children: /* @__PURE__ */ jsx("div", {
394
+ ref,
395
+ ...props,
396
+ children
397
+ })
398
+ });
399
+ });
400
+ CredentialOverlayRoot.displayName = "CredentialOverlay.Root";
401
+
402
+ //#endregion
403
+ //#region src/ui/core/Slot.tsx
404
+ /**
405
+ * Compose multiple refs into a single callback ref
406
+ * Handles function refs, object refs, and undefined refs safely
407
+ */
408
+ function composeRefs(...refs) {
409
+ return (instance) => {
410
+ for (const ref of refs) if (typeof ref === "function") ref(instance);
411
+ else if (ref != null) ref.current = instance;
412
+ };
413
+ }
414
+ /**
415
+ * Compose event handlers, calling child handler first then slot handler
416
+ */
417
+ function composeEventHandlers(childHandler, slotHandler) {
418
+ if (!childHandler && !slotHandler) return void 0;
419
+ if (!childHandler) return slotHandler;
420
+ if (!slotHandler) return childHandler;
421
+ return (event) => {
422
+ childHandler(event);
423
+ slotHandler(event);
424
+ };
425
+ }
426
+ /**
427
+ * Merge props from slot and child element
428
+ * - Event handlers are composed (child first, then slot)
429
+ * - className is concatenated
430
+ * - style is shallow merged (child overrides slot)
431
+ * - Other props: child overrides slot
432
+ */
433
+ function mergeProps(slotProps, childProps) {
434
+ const merged = { ...slotProps };
435
+ for (const key in childProps) {
436
+ const slotValue = slotProps[key];
437
+ const childValue = childProps[key];
438
+ if (/^on[A-Z]/.test(key)) merged[key] = composeEventHandlers(childValue, slotValue);
439
+ else if (key === "className") merged[key] = [slotValue, childValue].filter(Boolean).join(" ");
440
+ else if (key === "style") merged[key] = {
441
+ ...slotValue,
442
+ ...childValue
443
+ };
444
+ else merged[key] = childValue;
445
+ }
446
+ return merged;
447
+ }
448
+ /**
449
+ * Slot - Enables the asChild pattern for composable components
450
+ *
451
+ * Renders the child element with merged props and refs from the slot.
452
+ * This enables consumers to use their own elements while inheriting
453
+ * behavior from headless components.
454
+ *
455
+ * Inspired by Radix UI's Slot primitive.
456
+ *
457
+ * @example
458
+ * ```tsx
459
+ * // Component with asChild support
460
+ * const Button = forwardRef<HTMLButtonElement, ButtonProps>(
461
+ * ({ asChild, ...props }, ref) => {
462
+ * const Comp = asChild ? Slot : 'button';
463
+ * return <Comp ref={ref} {...props} />;
464
+ * }
465
+ * );
466
+ *
467
+ * // Consumer renders their own element
468
+ * <Button asChild onClick={handleClick}>
469
+ * <a href="/link">Click me</a>
470
+ * </Button>
471
+ * // Renders: <a href="/link" onClick={handleClick}>Click me</a>
472
+ * ```
473
+ */
474
+ const Slot = forwardRef(({ children, ...slotProps }, forwardedRef) => {
475
+ const childArray = Children.toArray(children);
476
+ const slottableChild = childArray.find(isValidElement);
477
+ if (!slottableChild || !isValidElement(slottableChild)) return /* @__PURE__ */ jsx(Fragment, { children });
478
+ const child = slottableChild;
479
+ const grandchildren = childArray.flatMap((c) => c === slottableChild ? Children.toArray(child.props.children) : [c]);
480
+ return cloneElement(child, {
481
+ ...mergeProps(slotProps, child.props),
482
+ ref: forwardedRef ? composeRefs(forwardedRef, child.props.ref) : child.props.ref,
483
+ children: grandchildren.length > 0 ? grandchildren : void 0
484
+ });
485
+ });
486
+ Slot.displayName = "Slot";
487
+
488
+ //#endregion
489
+ //#region src/ui/core/credential/CredentialOverlayTrigger.tsx
490
+ /**
491
+ * CredentialOverlay.Trigger - Trigger element for showing/hiding the overlay
492
+ *
493
+ * Attaches hover and click handlers to show/hide the tooltip.
494
+ * Use asChild to render your own element.
495
+ *
496
+ * @example
497
+ * ```tsx
498
+ * <CredentialOverlay.Trigger asChild>
499
+ * <button className="bg-green-500 p-2 rounded-full">
500
+ * <CheckIcon />
501
+ * </button>
502
+ * </CredentialOverlay.Trigger>
503
+ * ```
504
+ */
505
+ const CredentialOverlayTrigger = forwardRef(({ asChild, children, onMouseEnter, onMouseLeave, onClick, ...props }, ref) => {
506
+ const { show, hide, toggle, hasCredentials } = useCredentialOverlayContext();
507
+ const triggerProps = {
508
+ onMouseEnter: useCallback((e) => {
509
+ show();
510
+ onMouseEnter?.(e);
511
+ }, [show, onMouseEnter]),
512
+ onMouseLeave: useCallback((e) => {
513
+ hide();
514
+ onMouseLeave?.(e);
515
+ }, [hide, onMouseLeave]),
516
+ onClick: useCallback((e) => {
517
+ toggle();
518
+ onClick?.(e);
519
+ }, [toggle, onClick]),
520
+ "aria-label": hasCredentials ? "View content credentials" : "No credentials found",
521
+ "aria-haspopup": "dialog",
522
+ ...props
523
+ };
524
+ if (asChild) return /* @__PURE__ */ jsx(Slot, {
525
+ ref,
526
+ ...triggerProps,
527
+ children
528
+ });
529
+ return /* @__PURE__ */ jsx("div", {
530
+ ref,
531
+ ...triggerProps,
532
+ children
533
+ });
534
+ });
535
+ CredentialOverlayTrigger.displayName = "CredentialOverlay.Trigger";
536
+
537
+ //#endregion
538
+ //#region src/ui/core/credential/index.ts
539
+ /**
540
+ * CredentialOverlay - Headless compound component for trigger + tooltip pattern
541
+ *
542
+ * @example
543
+ * ```tsx
544
+ * import { CredentialOverlay, Passport, CredentialBadge } from "@limboai/react";
545
+ *
546
+ * <div className="relative">
547
+ * <img src="/photo.jpg" />
548
+ *
549
+ * <CredentialOverlay.Root validation={validation} status={status}>
550
+ * <CredentialOverlay.Trigger className="absolute top-2 right-2">
551
+ * <CredentialBadge.Root status={status} hasCredentials={hasCredentials}>
552
+ * <CredentialBadge.Verified>
553
+ * <button className="bg-green-500 p-2 rounded-full">✓</button>
554
+ * </CredentialBadge.Verified>
555
+ * </CredentialBadge.Root>
556
+ * </CredentialOverlay.Trigger>
557
+ *
558
+ * <CredentialOverlay.Content className="absolute top-12 right-0 bg-white shadow-lg rounded-lg p-4">
559
+ * <Passport.Root validation={validation}>
560
+ * ...
561
+ * </Passport.Root>
562
+ * </CredentialOverlay.Content>
563
+ * </CredentialOverlay.Root>
564
+ * </div>
565
+ * ```
566
+ */
567
+ const CredentialOverlay = {
568
+ Root: CredentialOverlayRoot,
569
+ Trigger: CredentialOverlayTrigger,
570
+ Content: CredentialOverlayContent
571
+ };
572
+
573
+ //#endregion
574
+ //#region src/ui/core/passport/context/PassportContext.tsx
575
+ const PassportContext = createContext(null);
576
+
577
+ //#endregion
578
+ //#region src/ui/core/passport/hooks/usePassportContext.ts
579
+ /**
580
+ * Access the Passport context
581
+ *
582
+ * Must be used within a Passport.Root component
583
+ *
584
+ * @example
585
+ * ```tsx
586
+ * function MyCustomField() {
587
+ * const { issuer, issuedDate, copied, isPending } = usePassportContext();
588
+ * return (
589
+ * <div>
590
+ * <p>{issuer} - {issuedDate}</p>
591
+ * {isPending && <span>Copying...</span>}
592
+ * </div>
593
+ * );
594
+ * }
595
+ * ```
596
+ */
597
+ function usePassportContext() {
598
+ const context = useContext(PassportContext);
599
+ if (!context) throw new Error("usePassportContext must be used within a Passport.Root");
600
+ useDebugValue({
601
+ issuer: context.issuer,
602
+ copied: context.copied
603
+ });
604
+ return context;
605
+ }
606
+
607
+ //#endregion
608
+ //#region src/ui/core/passport/hooks/usePassportData.ts
609
+ /**
610
+ * Extract actions from c2pa.actions.v2 or c2pa.actions assertion
611
+ */
612
+ function extractActions(assertions) {
613
+ const actionsAssertion = assertions.find((a) => a.label === "c2pa.actions.v2" || a.label === "c2pa.actions");
614
+ if (!actionsAssertion?.value) return [];
615
+ const value = actionsAssertion.value;
616
+ if (value.actions && Array.isArray(value.actions)) return value.actions.map((a) => a.action).filter((action) => Boolean(action));
617
+ if (value.action) return [value.action];
618
+ return [];
619
+ }
620
+ /**
621
+ * Extract verified identities from cawg.identity assertion
622
+ * Supports both:
623
+ * - Direct format: value.verifiedIdentities
624
+ * - CAWG 1.1 Verifiable Credential format: value.credentialSubject.verifiedIdentities
625
+ */
626
+ function extractIdentities(assertions) {
627
+ const identityAssertion = assertions.find((a) => a.label === "cawg.identity");
628
+ if (!identityAssertion?.value) return [];
629
+ const value = identityAssertion.value;
630
+ if (value.credentialSubject?.verifiedIdentities && Array.isArray(value.credentialSubject.verifiedIdentities)) return value.credentialSubject.verifiedIdentities;
631
+ if (value.verifiedIdentities && Array.isArray(value.verifiedIdentities)) return value.verifiedIdentities;
632
+ return [];
633
+ }
634
+ /**
635
+ * Format a date string for display
636
+ */
637
+ function formatDate(dateString) {
638
+ return new Date(dateString).toLocaleDateString("en-US", {
639
+ year: "numeric",
640
+ month: "long",
641
+ day: "numeric"
642
+ });
643
+ }
644
+ /**
645
+ * Hook to extract and process data from a C2PA validation response
646
+ *
647
+ * Extracts issuer, date, actions, identities, and signers from the validation
648
+ * response for easy consumption by Passport components.
649
+ *
650
+ * @example
651
+ * ```tsx
652
+ * const { issuer, issuedDate, actions, verifiedIdentities, previousSigners } =
653
+ * usePassportData(validation);
654
+ *
655
+ * return (
656
+ * <div>
657
+ * <p>Issued by: {issuer}</p>
658
+ * <p>Date: {issuedDate}</p>
659
+ * </div>
660
+ * );
661
+ * ```
662
+ */
663
+ function usePassportData(validation) {
664
+ const data = useMemo(() => {
665
+ const activeManifest = validation.manifest_chain?.find((manifest) => manifest.label === validation.active_manifest_label) || validation.manifest_chain?.[0] || null;
666
+ const issuer = activeManifest?.signature_info.issuer || "Unknown";
667
+ const issuedDateRaw = activeManifest?.signature_info.time || null;
668
+ return {
669
+ issuer,
670
+ issuedDate: issuedDateRaw ? formatDate(issuedDateRaw) : "Date unknown",
671
+ issuedDateRaw,
672
+ actions: activeManifest?.assertions ? extractActions(activeManifest.assertions) : [],
673
+ verifiedIdentities: activeManifest?.assertions ? extractIdentities(activeManifest.assertions) : [],
674
+ previousSigners: validation.manifest_chain?.filter((m) => m.label !== validation.active_manifest_label) || [],
675
+ activeManifest,
676
+ validation
677
+ };
678
+ }, [validation]);
679
+ useDebugValue({
680
+ issuer: data.issuer,
681
+ hasActions: data.actions.length > 0
682
+ });
683
+ return data;
684
+ }
685
+
686
+ //#endregion
687
+ //#region src/ui/core/passport/PassportActions.tsx
688
+ /**
689
+ * Format action label for display
690
+ * e.g., "c2pa.edited" -> "Edited"
691
+ */
692
+ function formatActionLabel(action) {
693
+ const withoutNamespace = action.split(".").pop() || action;
694
+ return withoutNamespace.charAt(0).toUpperCase() + withoutNamespace.slice(1);
695
+ }
696
+ /**
697
+ * Passport.Actions - Display actions from the manifest
698
+ *
699
+ * Renders actions using a render prop for full styling control.
700
+ * Returns null if there are no actions.
701
+ *
702
+ * @example
703
+ * ```tsx
704
+ * <Passport.Actions className="mt-4">
705
+ * {(actions) => (
706
+ * <div className="flex gap-2">
707
+ * {actions.map((action) => (
708
+ * <span key={action} className="tag bg-gray-100 px-2 py-1 rounded">
709
+ * {action}
710
+ * </span>
711
+ * ))}
712
+ * </div>
713
+ * )}
714
+ * </Passport.Actions>
715
+ * ```
716
+ */
717
+ const PassportActions = forwardRef(({ children, ...props }, ref) => {
718
+ const { actions } = usePassportContext();
719
+ if (actions.length === 0) return null;
720
+ const formattedActions = actions.map(formatActionLabel);
721
+ if (typeof children !== "function") return /* @__PURE__ */ jsxs("div", {
722
+ ref,
723
+ ...props,
724
+ children: [/* @__PURE__ */ jsx("div", {
725
+ "data-passport-actions-label": true,
726
+ children: "Actions"
727
+ }), /* @__PURE__ */ jsx("div", {
728
+ "data-passport-actions-list": true,
729
+ children: formattedActions.map((action) => /* @__PURE__ */ jsx("span", {
730
+ "data-passport-action": true,
731
+ children: action
732
+ }, action))
733
+ })]
734
+ });
735
+ return /* @__PURE__ */ jsx("div", {
736
+ ref,
737
+ ...props,
738
+ children: children(formattedActions)
739
+ });
740
+ });
741
+ PassportActions.displayName = "Passport.Actions";
742
+
743
+ //#endregion
744
+ //#region src/ui/core/passport/PassportCopyButton.tsx
745
+ /**
746
+ * Passport.CopyButton - Copy validation JSON to clipboard
747
+ *
748
+ * Renders a button that copies the validation JSON to clipboard.
749
+ * Supports pending state from React 19's useActionState.
750
+ * Use asChild to provide your own button element.
751
+ *
752
+ * @example
753
+ * ```tsx
754
+ * // Default button
755
+ * <Passport.CopyButton className="btn" />
756
+ *
757
+ * // Custom button with asChild
758
+ * <Passport.CopyButton asChild>
759
+ * <button className="my-button">
760
+ * Copy JSON
761
+ * </button>
762
+ * </Passport.CopyButton>
763
+ *
764
+ * // Access pending state in custom component
765
+ * function CustomCopyButton() {
766
+ * const { copyJson, copied, isPending } = usePassportContext();
767
+ * return (
768
+ * <button onClick={copyJson} disabled={isPending}>
769
+ * {isPending ? "Copying..." : copied ? "Copied!" : "Copy"}
770
+ * </button>
771
+ * );
772
+ * }
773
+ * ```
774
+ */
775
+ const PassportCopyButton = forwardRef(({ asChild, children, onClick, disabled, ...props }, ref) => {
776
+ const { copyJson, copied, isPending } = usePassportContext();
777
+ const handleClick = (e) => {
778
+ copyJson();
779
+ onClick?.(e);
780
+ };
781
+ const commonProps = {
782
+ onClick: handleClick,
783
+ disabled: disabled || isPending,
784
+ "data-copied": copied || void 0,
785
+ "data-pending": isPending || void 0,
786
+ "aria-busy": isPending || void 0,
787
+ ...props
788
+ };
789
+ if (asChild && children) return /* @__PURE__ */ jsx(Slot, {
790
+ ref,
791
+ ...commonProps,
792
+ children
793
+ });
794
+ return /* @__PURE__ */ jsx("button", {
795
+ ref,
796
+ type: "button",
797
+ ...commonProps,
798
+ children: isPending ? "Copying..." : copied ? "Copied!" : "Copy JSON"
799
+ });
800
+ });
801
+ PassportCopyButton.displayName = "Passport.CopyButton";
802
+
803
+ //#endregion
804
+ //#region src/ui/core/passport/PassportField.tsx
805
+ const DEFAULT_LABELS = {
806
+ [PassportFieldName.Issuer]: "Issued by",
807
+ [PassportFieldName.Date]: "Date issued"
808
+ };
809
+ /**
810
+ * Passport.Field - Display a field from the passport data
811
+ *
812
+ * Automatically fills the value from context based on the field name.
813
+ * Use a render function for complete control over layout.
814
+ *
815
+ * @example
816
+ * ```tsx
817
+ * // Simple usage with default rendering
818
+ * <Passport.Field name="issuer" className="mb-2" />
819
+ *
820
+ * // Custom label
821
+ * <Passport.Field name="date" label="Signed on" />
822
+ *
823
+ * // Render function for custom layout
824
+ * <Passport.Field name="issuer">
825
+ * {({ label, value }) => (
826
+ * <div className="flex justify-between">
827
+ * <span className="font-bold">{label}</span>
828
+ * <span>{value}</span>
829
+ * </div>
830
+ * )}
831
+ * </Passport.Field>
832
+ * ```
833
+ */
834
+ const PassportField = forwardRef(({ name, label, children, ...props }, ref) => {
835
+ const context = usePassportContext();
836
+ const value = name === PassportFieldName.Issuer ? context.issuer : context.issuedDate;
837
+ const displayLabel = label ?? DEFAULT_LABELS[name];
838
+ if (typeof children === "function") return /* @__PURE__ */ jsx("div", {
839
+ ref,
840
+ ...props,
841
+ children: children({
842
+ label: displayLabel,
843
+ value
844
+ })
845
+ });
846
+ return /* @__PURE__ */ jsxs("div", {
847
+ ref,
848
+ ...props,
849
+ children: [/* @__PURE__ */ jsx("div", {
850
+ "data-passport-field-label": true,
851
+ children: displayLabel
852
+ }), /* @__PURE__ */ jsx("div", {
853
+ "data-passport-field-value": true,
854
+ children: value
855
+ })]
856
+ });
857
+ });
858
+ PassportField.displayName = "Passport.Field";
859
+
860
+ //#endregion
861
+ //#region src/ui/core/passport/PassportFooter.tsx
862
+ /**
863
+ * Passport.Footer - Footer slot for the Passport
864
+ *
865
+ * A simple div wrapper for footer content. Style it yourself.
866
+ *
867
+ * @example
868
+ * ```tsx
869
+ * <Passport.Footer className="mt-4 border-t pt-4 flex justify-end">
870
+ * <Passport.CopyButton asChild>
871
+ * <button className="btn-primary">Copy JSON</button>
872
+ * </Passport.CopyButton>
873
+ * </Passport.Footer>
874
+ * ```
875
+ */
876
+ const PassportFooter = forwardRef(({ children, ...props }, ref) => {
877
+ return /* @__PURE__ */ jsx("div", {
878
+ ref,
879
+ ...props,
880
+ children
881
+ });
882
+ });
883
+ PassportFooter.displayName = "Passport.Footer";
884
+
885
+ //#endregion
886
+ //#region src/ui/core/passport/PassportHeader.tsx
887
+ /**
888
+ * Passport.Header - Header slot for the Passport
889
+ *
890
+ * A simple div wrapper for header content. Style it yourself.
891
+ *
892
+ * @example
893
+ * ```tsx
894
+ * <Passport.Header className="flex items-center gap-2 mb-4">
895
+ * <Passport.Logo className="w-6 h-6" />
896
+ * <Passport.Title className="text-lg font-bold" />
897
+ * </Passport.Header>
898
+ * ```
899
+ */
900
+ const PassportHeader = forwardRef(({ children, ...props }, ref) => {
901
+ return /* @__PURE__ */ jsx("div", {
902
+ ref,
903
+ ...props,
904
+ children
905
+ });
906
+ });
907
+ PassportHeader.displayName = "Passport.Header";
908
+
909
+ //#endregion
910
+ //#region src/ui/core/passport/PassportIdentities.tsx
911
+ /**
912
+ * Convert VerifiedIdentity to IdentityInfo for easier rendering
913
+ */
914
+ function toIdentityInfo(identity) {
915
+ return {
916
+ username: identity.name,
917
+ provider: identity.provider.name,
918
+ uri: identity.uri,
919
+ verifiedAt: identity.verifiedAt
920
+ };
921
+ }
922
+ /**
923
+ * Passport.Identities - Display verified identities from the manifest
924
+ *
925
+ * Renders identities using a render prop for full styling control.
926
+ * Returns null if there are no identities.
927
+ *
928
+ * @example
929
+ * ```tsx
930
+ * <Passport.Identities className="mt-4">
931
+ * {(identities) => (
932
+ * <ul className="space-y-2">
933
+ * {identities.map((identity) => (
934
+ * <li key={identity.provider} className="flex items-center gap-2">
935
+ * {identity.provider === "LinkedIn" && <LinkedInIcon />}
936
+ * {identity.uri ? (
937
+ * <a href={identity.uri} target="_blank" rel="noopener noreferrer">
938
+ * {identity.username}
939
+ * </a>
940
+ * ) : (
941
+ * <span>{identity.username}</span>
942
+ * )}
943
+ * </li>
944
+ * ))}
945
+ * </ul>
946
+ * )}
947
+ * </Passport.Identities>
948
+ * ```
949
+ */
950
+ const PassportIdentities = forwardRef(({ children, ...props }, ref) => {
951
+ const { verifiedIdentities } = usePassportContext();
952
+ if (verifiedIdentities.length === 0) return null;
953
+ const identities = verifiedIdentities.map(toIdentityInfo);
954
+ const label = identities.length === 1 ? "Verified Identity" : `Verified Identities (${identities.length})`;
955
+ if (typeof children !== "function") return /* @__PURE__ */ jsxs("div", {
956
+ ref,
957
+ ...props,
958
+ children: [/* @__PURE__ */ jsx("div", {
959
+ "data-passport-identities-label": true,
960
+ children: label
961
+ }), /* @__PURE__ */ jsx("div", {
962
+ "data-passport-identities-list": true,
963
+ children: identities.map((identity) => /* @__PURE__ */ jsxs("div", {
964
+ "data-passport-identity": true,
965
+ children: [/* @__PURE__ */ jsx("span", {
966
+ "data-passport-identity-provider": true,
967
+ children: identity.provider
968
+ }), identity.uri ? /* @__PURE__ */ jsx("a", {
969
+ href: identity.uri,
970
+ target: "_blank",
971
+ rel: "noopener noreferrer",
972
+ "data-passport-identity-link": true,
973
+ children: identity.username
974
+ }) : /* @__PURE__ */ jsx("span", {
975
+ "data-passport-identity-username": true,
976
+ children: identity.username
977
+ })]
978
+ }, identity.provider))
979
+ })]
980
+ });
981
+ return /* @__PURE__ */ jsx("div", {
982
+ ref,
983
+ ...props,
984
+ children: children(identities)
985
+ });
986
+ });
987
+ PassportIdentities.displayName = "Passport.Identities";
988
+
989
+ //#endregion
990
+ //#region src/ui/core/passport/PassportLogo.tsx
991
+ /**
992
+ * Passport.Logo - Logo for the Passport
993
+ *
994
+ * Renders the Content Credentials logo by default.
995
+ * Use asChild to provide your own logo element.
996
+ *
997
+ * @example
998
+ * ```tsx
999
+ * // Default logo
1000
+ * <Passport.Logo className="w-6 h-6" />
1001
+ *
1002
+ * // Custom logo with asChild
1003
+ * <Passport.Logo asChild>
1004
+ * <img src="/my-logo.svg" alt="Logo" className="w-6 h-6" />
1005
+ * </Passport.Logo>
1006
+ * ```
1007
+ */
1008
+ const PassportLogo = forwardRef(({ asChild, children, ...props }, ref) => {
1009
+ if (asChild && children) return /* @__PURE__ */ jsx(Slot, {
1010
+ ref,
1011
+ ...props,
1012
+ children
1013
+ });
1014
+ return /* @__PURE__ */ jsx(CRLogo, { ...props });
1015
+ });
1016
+ PassportLogo.displayName = "Passport.Logo";
1017
+
1018
+ //#endregion
1019
+ //#region src/ui/core/passport/PassportRoot.tsx
1020
+ /**
1021
+ * Passport.Root - Provider wrapper for the Passport compound component
1022
+ *
1023
+ * Provides context with processed validation data to all child components.
1024
+ * Uses React 19's useTransition for non-blocking copy operations.
1025
+ *
1026
+ * @example
1027
+ * ```tsx
1028
+ * <Passport.Root validation={validation} className="my-tooltip bg-white p-4 rounded">
1029
+ * <Passport.Header />
1030
+ * <Passport.Field name="issuer" />
1031
+ * <Passport.Footer />
1032
+ * </Passport.Root>
1033
+ * ```
1034
+ */
1035
+ const PassportRoot = forwardRef(({ validation, children, ...props }, ref) => {
1036
+ const titleId = useId();
1037
+ const passportData = usePassportData(validation);
1038
+ const [copied, setCopied] = useState(false);
1039
+ const [isPending, startTransition] = useTransition();
1040
+ const copyJson = useCallback(async () => {
1041
+ startTransition(async () => {
1042
+ try {
1043
+ await navigator.clipboard.writeText(JSON.stringify(validation, null, 2));
1044
+ setCopied(true);
1045
+ setTimeout(() => setCopied(false), 2e3);
1046
+ } catch (error) {
1047
+ console.error("Failed to copy:", error);
1048
+ }
1049
+ });
1050
+ return true;
1051
+ }, [validation]);
1052
+ const contextValue = useMemo(() => ({
1053
+ ...passportData,
1054
+ copyJson,
1055
+ copied,
1056
+ isPending,
1057
+ titleId
1058
+ }), [
1059
+ passportData,
1060
+ copyJson,
1061
+ copied,
1062
+ isPending,
1063
+ titleId
1064
+ ]);
1065
+ return /* @__PURE__ */ jsx(PassportContext.Provider, {
1066
+ value: contextValue,
1067
+ children: /* @__PURE__ */ jsx("div", {
1068
+ ref,
1069
+ role: "dialog",
1070
+ "aria-labelledby": titleId,
1071
+ ...props,
1072
+ children
1073
+ })
1074
+ });
1075
+ });
1076
+ PassportRoot.displayName = "Passport.Root";
1077
+
1078
+ //#endregion
1079
+ //#region src/ui/core/passport/PassportSigners.tsx
1080
+ /**
1081
+ * Passport.Signers - Display previous signers in the manifest chain
1082
+ *
1083
+ * Renders signers using a render prop for full styling control.
1084
+ * Returns null if there are no previous signers.
1085
+ *
1086
+ * @example
1087
+ * ```tsx
1088
+ * <Passport.Signers className="mt-4">
1089
+ * {(signers) => (
1090
+ * <ul className="space-y-1">
1091
+ * {signers.map((signer) => (
1092
+ * <li key={signer.id} className="text-sm text-gray-600">
1093
+ * {signer.label}
1094
+ * </li>
1095
+ * ))}
1096
+ * </ul>
1097
+ * )}
1098
+ * </Passport.Signers>
1099
+ * ```
1100
+ */
1101
+ const PassportSigners = forwardRef(({ children, ...props }, ref) => {
1102
+ const { previousSigners } = usePassportContext();
1103
+ if (previousSigners.length === 0) return null;
1104
+ const signers = previousSigners.map((manifest) => ({
1105
+ id: manifest.instance_id,
1106
+ label: manifest.signature_info.issuer,
1107
+ signedAt: manifest.signature_info.time ?? void 0
1108
+ }));
1109
+ const label = signers.length === 1 ? "Previous Signer" : `Previous Signers (${signers.length})`;
1110
+ if (typeof children !== "function") return /* @__PURE__ */ jsxs("div", {
1111
+ ref,
1112
+ ...props,
1113
+ children: [/* @__PURE__ */ jsx("div", {
1114
+ "data-passport-signers-label": true,
1115
+ children: label
1116
+ }), /* @__PURE__ */ jsx("div", {
1117
+ "data-passport-signers-list": true,
1118
+ children: signers.map((signer) => /* @__PURE__ */ jsx("div", {
1119
+ "data-passport-signer": true,
1120
+ children: signer.label
1121
+ }, signer.id))
1122
+ })]
1123
+ });
1124
+ return /* @__PURE__ */ jsx("div", {
1125
+ ref,
1126
+ ...props,
1127
+ children: children(signers)
1128
+ });
1129
+ });
1130
+ PassportSigners.displayName = "Passport.Signers";
1131
+
1132
+ //#endregion
1133
+ //#region src/ui/core/passport/PassportTitle.tsx
1134
+ /**
1135
+ * Passport.Title - Title for the Passport
1136
+ *
1137
+ * Renders a heading with the passport title. Defaults to "Content Credentials"
1138
+ * if no children are provided.
1139
+ *
1140
+ * @example
1141
+ * ```tsx
1142
+ * // Default title
1143
+ * <Passport.Title className="text-lg font-bold" />
1144
+ *
1145
+ * // Custom title
1146
+ * <Passport.Title className="text-lg font-bold">
1147
+ * My Custom Title
1148
+ * </Passport.Title>
1149
+ *
1150
+ * // With asChild
1151
+ * <Passport.Title asChild>
1152
+ * <h1 className="custom-heading">Content Credentials</h1>
1153
+ * </Passport.Title>
1154
+ * ```
1155
+ */
1156
+ const PassportTitle = forwardRef(({ asChild, children, ...props }, ref) => {
1157
+ const Comp = asChild ? Slot : "h2";
1158
+ const content = children ?? "Content Credentials";
1159
+ return /* @__PURE__ */ jsx(Comp, {
1160
+ ref,
1161
+ id: "passport-title",
1162
+ ...props,
1163
+ children: content
1164
+ });
1165
+ });
1166
+ PassportTitle.displayName = "Passport.Title";
1167
+
1168
+ //#endregion
1169
+ //#region src/ui/core/passport/index.ts
1170
+ /**
1171
+ * Passport - Headless compound component for displaying C2PA content credentials
1172
+ *
1173
+ * @example
1174
+ * ```tsx
1175
+ * import { Passport } from "@limboai/react";
1176
+ *
1177
+ * <Passport.Root validation={validation} className="my-tooltip">
1178
+ * <Passport.Header className="flex items-center gap-2">
1179
+ * <Passport.Logo className="w-6 h-6" />
1180
+ * <Passport.Title className="text-lg font-bold" />
1181
+ * </Passport.Header>
1182
+ *
1183
+ * <Passport.Field name="issuer" className="my-field" />
1184
+ * <Passport.Field name="date" className="my-field" />
1185
+ *
1186
+ * <Passport.Actions className="mt-4">
1187
+ * {(actions) => actions.map(a => <span key={a} className="tag">{a}</span>)}
1188
+ * </Passport.Actions>
1189
+ *
1190
+ * <Passport.Identities>
1191
+ * {(identities) => identities.map(id => <MyIdentityCard key={id.provider} {...id} />)}
1192
+ * </Passport.Identities>
1193
+ *
1194
+ * <Passport.Signers>
1195
+ * {(signers) => signers.map(s => <MySignerRow key={s.id} {...s} />)}
1196
+ * </Passport.Signers>
1197
+ *
1198
+ * <Passport.Footer className="mt-4 border-t pt-4">
1199
+ * <Passport.CopyButton asChild>
1200
+ * <button className="btn-primary">Copy JSON</button>
1201
+ * </Passport.CopyButton>
1202
+ * </Passport.Footer>
1203
+ * </Passport.Root>
1204
+ * ```
1205
+ */
1206
+ const Passport = {
1207
+ Root: PassportRoot,
1208
+ Header: PassportHeader,
1209
+ Title: PassportTitle,
1210
+ Logo: PassportLogo,
1211
+ Field: PassportField,
1212
+ Actions: PassportActions,
1213
+ Identities: PassportIdentities,
1214
+ Signers: PassportSigners,
1215
+ Footer: PassportFooter,
1216
+ CopyButton: PassportCopyButton
1217
+ };
1218
+
1219
+ //#endregion
1220
+ //#region src/ui/default/constants.ts
1221
+ const BadgePosition = {
1222
+ TopRight: "top-right",
1223
+ TopLeft: "top-left",
1224
+ BottomRight: "bottom-right",
1225
+ BottomLeft: "bottom-left"
1226
+ };
1227
+ const PassportVariant$1 = {
1228
+ Basic: "basic",
1229
+ Full: "full"
1230
+ };
1231
+ const Theme = {
1232
+ Light: "light",
1233
+ Dark: "dark"
1234
+ };
1235
+ const wrapperStyle = {
1236
+ position: "relative",
1237
+ display: "inline-block"
1238
+ };
1239
+
1240
+ //#endregion
1241
+ //#region src/ui/default/LimboBadge.tsx
1242
+ const badgeStyles = {
1243
+ light: {
1244
+ backgroundColor: "#ffffff",
1245
+ borderRadius: "9999px",
1246
+ padding: "8px",
1247
+ boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
1248
+ cursor: "pointer",
1249
+ border: "1px solid #e5e7eb",
1250
+ display: "flex",
1251
+ alignItems: "center",
1252
+ justifyContent: "center"
1253
+ },
1254
+ dark: {
1255
+ backgroundColor: "#1f2937",
1256
+ borderRadius: "9999px",
1257
+ padding: "8px",
1258
+ boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3)",
1259
+ cursor: "pointer",
1260
+ border: "1px solid #374151",
1261
+ display: "flex",
1262
+ alignItems: "center",
1263
+ justifyContent: "center"
1264
+ }
1265
+ };
1266
+ const badgePositionStyles = {
1267
+ "top-right": {
1268
+ position: "absolute",
1269
+ top: "8px",
1270
+ right: "8px"
1271
+ },
1272
+ "top-left": {
1273
+ position: "absolute",
1274
+ top: "8px",
1275
+ left: "8px"
1276
+ },
1277
+ "bottom-right": {
1278
+ position: "absolute",
1279
+ bottom: "8px",
1280
+ right: "8px"
1281
+ },
1282
+ "bottom-left": {
1283
+ position: "absolute",
1284
+ bottom: "8px",
1285
+ left: "8px"
1286
+ }
1287
+ };
1288
+ /**
1289
+ * LimboBadge - Pre-styled badge component with CRLogo
1290
+ *
1291
+ * A ready-to-use badge for displaying content credentials status.
1292
+ */
1293
+ const LimboBadge = ({ position = BadgePosition.TopRight, theme = Theme.Light, onClick, onMouseEnter, onMouseLeave, style }) => {
1294
+ return /* @__PURE__ */ jsx("button", {
1295
+ type: "button",
1296
+ onClick,
1297
+ onMouseEnter,
1298
+ onMouseLeave,
1299
+ style: {
1300
+ ...badgeStyles[theme],
1301
+ ...badgePositionStyles[position],
1302
+ ...style
1303
+ },
1304
+ "aria-label": "View content credentials",
1305
+ children: /* @__PURE__ */ jsx(CRLogo, { size: 16 })
1306
+ });
1307
+ };
1308
+ LimboBadge.displayName = "LimboBadge";
1309
+
1310
+ //#endregion
1311
+ //#region src/ui/media/hooks/useHeicConversion.ts
1312
+ const isHeic = (src) => /\.(heic|heif)$/i.test(src);
1313
+ async function convertHeicToJpeg(file, signal) {
1314
+ if (typeof window === "undefined") return void 0;
1315
+ const { default: heic2any } = await import("heic2any");
1316
+ if (signal.aborted) return void 0;
1317
+ const res = await heic2any({
1318
+ blob: file,
1319
+ toType: "image/jpeg"
1320
+ });
1321
+ const blob = res instanceof Blob ? res : res[0];
1322
+ if (!blob) throw new Error("HEIC to JPEG conversion failed");
1323
+ return blob;
1324
+ }
1325
+ /**
1326
+ * Hook for automatic HEIC to JPEG conversion
1327
+ *
1328
+ * Uses AbortController for proper cleanup and race condition handling.
1329
+ * Automatically revokes blob URLs on cleanup.
1330
+ *
1331
+ * @example
1332
+ * ```tsx
1333
+ * const { displaySrc, isConverting, error } = useHeicConversion({
1334
+ * src: imageUrl,
1335
+ * enabled: true,
1336
+ * });
1337
+ *
1338
+ * return (
1339
+ * <img
1340
+ * src={displaySrc}
1341
+ * data-converting={isConverting || undefined}
1342
+ * />
1343
+ * );
1344
+ * ```
1345
+ */
1346
+ function useHeicConversion({ src, enabled = true }) {
1347
+ const [displaySrc, setDisplaySrc] = useState(src);
1348
+ const [isConverting, setIsConverting] = useState(false);
1349
+ const [error, setError] = useState(null);
1350
+ useEffect(() => {
1351
+ if (!src || !enabled || !isHeic(src)) {
1352
+ setDisplaySrc(src);
1353
+ setIsConverting(false);
1354
+ setError(null);
1355
+ return;
1356
+ }
1357
+ const controller = new AbortController();
1358
+ const { signal } = controller;
1359
+ let blobUrl = null;
1360
+ setIsConverting(true);
1361
+ setError(null);
1362
+ const convert = async () => {
1363
+ try {
1364
+ const response = await fetch(src, { signal });
1365
+ if (!response.ok) throw new Error(`Fetch failed: ${response.status}`);
1366
+ const blob = await convertHeicToJpeg(await response.blob(), signal);
1367
+ if (signal.aborted || !blob) return;
1368
+ blobUrl = URL.createObjectURL(blob);
1369
+ setDisplaySrc(blobUrl);
1370
+ setIsConverting(false);
1371
+ } catch (e) {
1372
+ if (signal.aborted) return;
1373
+ setError(e instanceof Error ? e.message : "HEIC conversion failed");
1374
+ setDisplaySrc(src);
1375
+ setIsConverting(false);
1376
+ }
1377
+ };
1378
+ convert();
1379
+ return () => {
1380
+ controller.abort();
1381
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
1382
+ };
1383
+ }, [src, enabled]);
1384
+ useDebugValue({
1385
+ isConverting,
1386
+ hasError: !!error
1387
+ });
1388
+ return {
1389
+ displaySrc,
1390
+ isConverting,
1391
+ error
1392
+ };
1393
+ }
1394
+
1395
+ //#endregion
1396
+ //#region src/ui/media/context/MediaContext.tsx
1397
+ const MediaContext = createContext(null);
1398
+ MediaContext.displayName = "MediaContext";
1399
+
1400
+ //#endregion
1401
+ //#region src/ui/media/hooks/useMediaContext.ts
1402
+ /**
1403
+ * Hook to access Media context
1404
+ *
1405
+ * Use this hook inside MediaImage/MediaVideo render props to access
1406
+ * the media context including validation data and overlay visibility state.
1407
+ *
1408
+ * @example
1409
+ * ```tsx
1410
+ * const { validation, status, isVisible, toggle } = useMediaContext();
1411
+ * ```
1412
+ */
1413
+ function useMediaContext() {
1414
+ const context = useContext(MediaContext);
1415
+ if (!context) throw new Error("useMediaContext must be used within a MediaImage or MediaVideo component");
1416
+ useDebugValue({
1417
+ status: context.status,
1418
+ isVisible: context.isVisible
1419
+ });
1420
+ return context;
1421
+ }
1422
+
1423
+ //#endregion
1424
+ //#region src/ui/media/hooks/useMediaOverlay.ts
1425
+ /**
1426
+ * Shared hook for overlay state management in media components
1427
+ *
1428
+ * Extracts common logic used by both MediaImage and MediaVideo components
1429
+ * for managing tooltip visibility and building render props.
1430
+ *
1431
+ * @example
1432
+ * ```tsx
1433
+ * const { hasCredentials, contextValue, badgeProps, tooltipProps } = useMediaOverlay({
1434
+ * validation,
1435
+ * validationStatus,
1436
+ * hideDelay: 300,
1437
+ * });
1438
+ * ```
1439
+ */
1440
+ function useMediaOverlay({ validation, validationStatus, hideDelay = 300 }) {
1441
+ const hasCredentials = validation?.has_credentials ?? false;
1442
+ const { isVisible, show, hide, toggle } = useTooltipVisibility({
1443
+ hasCredentials,
1444
+ hideDelay
1445
+ });
1446
+ const contextValue = {
1447
+ validation,
1448
+ status: validationStatus,
1449
+ hasCredentials,
1450
+ isVisible,
1451
+ show,
1452
+ hide,
1453
+ toggle
1454
+ };
1455
+ const badgeProps = {
1456
+ status: validationStatus,
1457
+ hasCredentials,
1458
+ isVisible,
1459
+ show,
1460
+ hide,
1461
+ toggle
1462
+ };
1463
+ const tooltipProps = validation ? {
1464
+ validation,
1465
+ isVisible,
1466
+ onMouseEnter: show,
1467
+ onClose: hide
1468
+ } : null;
1469
+ useDebugValue({
1470
+ isVisible,
1471
+ hasCredentials
1472
+ });
1473
+ return {
1474
+ hasCredentials,
1475
+ contextValue,
1476
+ badgeProps,
1477
+ tooltipProps
1478
+ };
1479
+ }
1480
+
1481
+ //#endregion
1482
+ //#region src/ui/media/MediaImage.tsx
1483
+ /**
1484
+ * MediaImage - Headless image component with built-in credential overlay support
1485
+ *
1486
+ * A minimal image component that handles HEIC to JPEG conversion and provides
1487
+ * built-in support for displaying credential badges and tooltips via render props.
1488
+ *
1489
+ * This component is headless - it applies no styles. Users must provide
1490
+ * all styling through className, style props, or render functions.
1491
+ *
1492
+ * @example
1493
+ * ```tsx
1494
+ * // Basic usage - just the image with HEIC support
1495
+ * <MediaImage src="/photo.heic" alt="Photo" className="w-full h-auto" />
1496
+ *
1497
+ * // With built-in overlay (using render props)
1498
+ * <MediaImage
1499
+ * src="/photo.jpg"
1500
+ * alt="Photo"
1501
+ * validation={validation}
1502
+ * validationStatus="success"
1503
+ * wrapperClassName="relative inline-block"
1504
+ * className="w-80 h-60 object-cover rounded-lg"
1505
+ * renderBadge={({ status, hasCredentials, toggle }) => (
1506
+ * <button
1507
+ * onClick={toggle}
1508
+ * className="absolute top-2 right-2 bg-green-500 rounded-full p-2"
1509
+ * >
1510
+ * <CRLogo size={16} />
1511
+ * </button>
1512
+ * )}
1513
+ * renderTooltip={({ validation, isVisible, onClose }) =>
1514
+ * isVisible && (
1515
+ * <div className="absolute top-14 right-0 bg-white shadow-lg rounded-lg p-4">
1516
+ * <Passport.Root validation={validation}>
1517
+ * <Passport.Header>...</Passport.Header>
1518
+ * <Passport.Field name="issuer" />
1519
+ * <Passport.Footer>
1520
+ * <Passport.CopyButton />
1521
+ * </Passport.Footer>
1522
+ * </Passport.Root>
1523
+ * </div>
1524
+ * )
1525
+ * }
1526
+ * />
1527
+ *
1528
+ * // With compound components (external overlay)
1529
+ * <div className="relative">
1530
+ * <MediaImage src="/photo.jpg" alt="Photo" className="w-full" />
1531
+ * <CredentialOverlay.Root validation={validation} status="success">
1532
+ * <CredentialOverlay.Trigger className="absolute top-2 right-2">
1533
+ * <CredentialBadge.Root status="success" hasCredentials={true}>
1534
+ * <CredentialBadge.Verified>
1535
+ * <button className="bg-green-500 p-2 rounded-full">OK</button>
1536
+ * </CredentialBadge.Verified>
1537
+ * </CredentialBadge.Root>
1538
+ * </CredentialOverlay.Trigger>
1539
+ * <CredentialOverlay.Content className="absolute top-12 right-0">
1540
+ * <Passport.Root validation={validation}>...</Passport.Root>
1541
+ * </CredentialOverlay.Content>
1542
+ * </CredentialOverlay.Root>
1543
+ * </div>
1544
+ * ```
1545
+ */
1546
+ const MediaImage = forwardRef(({ validation = null, validationStatus = "idle", renderBadge, renderTooltip, wrapperClassName, wrapperStyle, hideDelay = 300, enableHeicConversion = true, src, ...imgProps }, ref) => {
1547
+ const { displaySrc, isConverting } = useHeicConversion({
1548
+ src,
1549
+ enabled: enableHeicConversion
1550
+ });
1551
+ const { contextValue, badgeProps, tooltipProps } = useMediaOverlay({
1552
+ validation,
1553
+ validationStatus,
1554
+ hideDelay
1555
+ });
1556
+ const imageElement = /* @__PURE__ */ jsx("img", {
1557
+ ref,
1558
+ ...imgProps,
1559
+ src: displaySrc,
1560
+ alt: imgProps.alt || "",
1561
+ "data-converting": isConverting || void 0
1562
+ });
1563
+ if (!renderBadge && !renderTooltip) return imageElement;
1564
+ return /* @__PURE__ */ jsx(MediaContext.Provider, {
1565
+ value: contextValue,
1566
+ children: /* @__PURE__ */ jsxs("div", {
1567
+ className: wrapperClassName,
1568
+ style: wrapperStyle,
1569
+ children: [
1570
+ imageElement,
1571
+ renderBadge?.(badgeProps),
1572
+ tooltipProps && renderTooltip?.(tooltipProps)
1573
+ ]
1574
+ })
1575
+ });
1576
+ });
1577
+ MediaImage.displayName = "MediaImage";
1578
+
1579
+ //#endregion
1580
+ //#region src/ui/media/MediaVideo.tsx
1581
+ /**
1582
+ * MediaVideo - Headless video component with built-in credential overlay support
1583
+ *
1584
+ * A minimal video component for use with C2PA credentials. Provides built-in
1585
+ * support for displaying credential badges and tooltips via render props.
1586
+ *
1587
+ * This component is headless - it applies no styles. Users must provide
1588
+ * all styling through className, style props, or render functions.
1589
+ *
1590
+ * @example
1591
+ * ```tsx
1592
+ * // Basic usage - just the video
1593
+ * <MediaVideo src="/video.mp4" controls className="w-full h-auto" />
1594
+ *
1595
+ * // With built-in overlay (using render props)
1596
+ * <MediaVideo
1597
+ * src="/video.mp4"
1598
+ * controls
1599
+ * validation={validation}
1600
+ * validationStatus="success"
1601
+ * wrapperClassName="relative inline-block"
1602
+ * className="w-80 h-60 object-cover rounded-lg"
1603
+ * renderBadge={({ status, hasCredentials, toggle }) => (
1604
+ * <button
1605
+ * onClick={toggle}
1606
+ * className="absolute top-2 right-2 bg-green-500 rounded-full p-2"
1607
+ * >
1608
+ * <CRLogo size={16} />
1609
+ * </button>
1610
+ * )}
1611
+ * renderTooltip={({ validation, isVisible, onClose }) =>
1612
+ * isVisible && (
1613
+ * <div className="absolute top-14 right-0 bg-white shadow-lg rounded-lg p-4">
1614
+ * <Passport.Root validation={validation}>
1615
+ * <Passport.Header>...</Passport.Header>
1616
+ * <Passport.Field name="issuer" />
1617
+ * <Passport.Footer>
1618
+ * <Passport.CopyButton />
1619
+ * </Passport.Footer>
1620
+ * </Passport.Root>
1621
+ * </div>
1622
+ * )
1623
+ * }
1624
+ * />
1625
+ *
1626
+ * // With compound components (external overlay)
1627
+ * <div className="relative">
1628
+ * <MediaVideo src="/video.mp4" controls className="w-full" />
1629
+ * <CredentialOverlay.Root validation={validation} status="success">
1630
+ * <CredentialOverlay.Trigger className="absolute top-2 right-2">
1631
+ * <CredentialBadge.Root status="success" hasCredentials={true}>
1632
+ * <CredentialBadge.Verified>
1633
+ * <button className="bg-green-500 p-2 rounded-full">OK</button>
1634
+ * </CredentialBadge.Verified>
1635
+ * </CredentialBadge.Root>
1636
+ * </CredentialOverlay.Trigger>
1637
+ * <CredentialOverlay.Content className="absolute top-12 right-0">
1638
+ * <Passport.Root validation={validation}>...</Passport.Root>
1639
+ * </CredentialOverlay.Content>
1640
+ * </CredentialOverlay.Root>
1641
+ * </div>
1642
+ * ```
1643
+ */
1644
+ const MediaVideo = forwardRef(({ validation = null, validationStatus = "idle", renderBadge, renderTooltip, wrapperClassName, wrapperStyle, hideDelay = 300, ...videoProps }, ref) => {
1645
+ const { contextValue, badgeProps, tooltipProps } = useMediaOverlay({
1646
+ validation,
1647
+ validationStatus,
1648
+ hideDelay
1649
+ });
1650
+ const videoElement = /* @__PURE__ */ jsx("video", {
1651
+ ref,
1652
+ ...videoProps
1653
+ });
1654
+ if (!renderBadge && !renderTooltip) return videoElement;
1655
+ return /* @__PURE__ */ jsx(MediaContext.Provider, {
1656
+ value: contextValue,
1657
+ children: /* @__PURE__ */ jsxs("div", {
1658
+ className: wrapperClassName,
1659
+ style: wrapperStyle,
1660
+ children: [
1661
+ videoElement,
1662
+ renderBadge?.(badgeProps),
1663
+ tooltipProps && renderTooltip?.(tooltipProps)
1664
+ ]
1665
+ })
1666
+ });
1667
+ });
1668
+ MediaVideo.displayName = "MediaVideo";
1669
+
1670
+ //#endregion
1671
+ //#region src/ui/default/LimboPassport.tsx
1672
+ const tooltipPositionStyles = {
1673
+ "top-right": {
1674
+ position: "absolute",
1675
+ top: "56px",
1676
+ right: "0",
1677
+ zIndex: 10
1678
+ },
1679
+ "top-left": {
1680
+ position: "absolute",
1681
+ top: "56px",
1682
+ left: "0",
1683
+ zIndex: 10
1684
+ },
1685
+ "bottom-right": {
1686
+ position: "absolute",
1687
+ bottom: "56px",
1688
+ right: "0",
1689
+ zIndex: 10
1690
+ },
1691
+ "bottom-left": {
1692
+ position: "absolute",
1693
+ bottom: "56px",
1694
+ left: "0",
1695
+ zIndex: 10
1696
+ }
1697
+ };
1698
+ const passportContainerStyles = {
1699
+ light: {
1700
+ backgroundColor: "#ffffff",
1701
+ border: "1px solid #e5e7eb",
1702
+ borderRadius: "12px",
1703
+ padding: "20px",
1704
+ width: "320px",
1705
+ boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
1706
+ fontFamily: "system-ui, -apple-system, sans-serif"
1707
+ },
1708
+ dark: {
1709
+ backgroundColor: "#111827",
1710
+ border: "1px solid #374151",
1711
+ borderRadius: "12px",
1712
+ padding: "20px",
1713
+ width: "320px",
1714
+ boxShadow: "0 10px 15px -3px rgb(0 0 0 / 0.3), 0 4px 6px -4px rgb(0 0 0 / 0.3)",
1715
+ fontFamily: "system-ui, -apple-system, sans-serif"
1716
+ }
1717
+ };
1718
+ const fieldLabelStyles = {
1719
+ light: {
1720
+ fontSize: "11px",
1721
+ fontWeight: 500,
1722
+ color: "#6b7280",
1723
+ textTransform: "uppercase",
1724
+ letterSpacing: "0.05em",
1725
+ marginBottom: "4px"
1726
+ },
1727
+ dark: {
1728
+ fontSize: "11px",
1729
+ fontWeight: 500,
1730
+ color: "#9ca3af",
1731
+ textTransform: "uppercase",
1732
+ letterSpacing: "0.05em",
1733
+ marginBottom: "4px"
1734
+ }
1735
+ };
1736
+ const fieldValueStyles = {
1737
+ light: {
1738
+ fontSize: "13px",
1739
+ color: "#111827"
1740
+ },
1741
+ dark: {
1742
+ fontSize: "13px",
1743
+ color: "#f3f4f6"
1744
+ }
1745
+ };
1746
+ const passportHeaderStyles = {
1747
+ container: {
1748
+ display: "flex",
1749
+ alignItems: "center",
1750
+ gap: "8px",
1751
+ marginBottom: "16px"
1752
+ },
1753
+ logo: {
1754
+ width: "20px",
1755
+ height: "20px"
1756
+ },
1757
+ title: {
1758
+ light: {
1759
+ fontSize: "16px",
1760
+ fontWeight: 600,
1761
+ color: "#111827"
1762
+ },
1763
+ dark: {
1764
+ fontSize: "16px",
1765
+ fontWeight: 600,
1766
+ color: "#ffffff"
1767
+ }
1768
+ }
1769
+ };
1770
+ const passportFooterStyles = {
1771
+ container: {
1772
+ light: {
1773
+ marginTop: "16px",
1774
+ paddingTop: "16px",
1775
+ borderTop: "1px solid #f3f4f6",
1776
+ display: "flex",
1777
+ justifyContent: "space-between",
1778
+ alignItems: "center"
1779
+ },
1780
+ dark: {
1781
+ marginTop: "16px",
1782
+ paddingTop: "16px",
1783
+ borderTop: "1px solid #374151",
1784
+ display: "flex",
1785
+ justifyContent: "space-between",
1786
+ alignItems: "center"
1787
+ }
1788
+ },
1789
+ copyButton: {
1790
+ light: {
1791
+ display: "inline-flex",
1792
+ alignItems: "center",
1793
+ gap: "6px",
1794
+ fontSize: "13px",
1795
+ fontWeight: 500,
1796
+ color: "#4b5563",
1797
+ cursor: "pointer",
1798
+ backgroundColor: "transparent",
1799
+ border: "none",
1800
+ padding: 0
1801
+ },
1802
+ dark: {
1803
+ display: "inline-flex",
1804
+ alignItems: "center",
1805
+ gap: "6px",
1806
+ fontSize: "13px",
1807
+ fontWeight: 500,
1808
+ color: "#9ca3af",
1809
+ cursor: "pointer",
1810
+ backgroundColor: "transparent",
1811
+ border: "none",
1812
+ padding: 0
1813
+ }
1814
+ },
1815
+ closeButton: {
1816
+ light: {
1817
+ fontSize: "13px",
1818
+ color: "#9ca3af",
1819
+ backgroundColor: "transparent",
1820
+ border: "none",
1821
+ cursor: "pointer"
1822
+ },
1823
+ dark: {
1824
+ fontSize: "13px",
1825
+ color: "#6b7280",
1826
+ backgroundColor: "transparent",
1827
+ border: "none",
1828
+ cursor: "pointer"
1829
+ }
1830
+ }
1831
+ };
1832
+ const fieldContainerStyle = { marginBottom: "12px" };
1833
+ const identityStyles = {
1834
+ container: {
1835
+ display: "flex",
1836
+ flexDirection: "column",
1837
+ gap: "6px"
1838
+ },
1839
+ item: {
1840
+ display: "flex",
1841
+ alignItems: "center",
1842
+ gap: "8px"
1843
+ },
1844
+ link: {
1845
+ color: "#3b82f6",
1846
+ textDecoration: "none"
1847
+ }
1848
+ };
1849
+ const signerStyles = { container: {
1850
+ display: "flex",
1851
+ flexDirection: "column",
1852
+ gap: "4px"
1853
+ } };
1854
+ const actionTagStyles = {
1855
+ light: {
1856
+ backgroundColor: "#f3f4f6",
1857
+ color: "#374151",
1858
+ fontSize: "12px",
1859
+ padding: "2px 8px",
1860
+ borderRadius: "4px"
1861
+ },
1862
+ dark: {
1863
+ backgroundColor: "#374151",
1864
+ color: "#d1d5db",
1865
+ fontSize: "12px",
1866
+ padding: "2px 8px",
1867
+ borderRadius: "4px"
1868
+ }
1869
+ };
1870
+ const actionsContainerStyle = {
1871
+ display: "flex",
1872
+ flexWrap: "wrap",
1873
+ gap: "6px"
1874
+ };
1875
+ /**
1876
+ * LimboPassport - Pre-styled passport tooltip component
1877
+ *
1878
+ * Renders a passport with configurable variant (basic or full).
1879
+ */
1880
+ const LimboPassport = ({ validation, isVisible, theme = Theme.Light, position = BadgePosition.TopRight, variant = PassportVariant$1.Basic, showDate = true, onMouseEnter, onClose, style }) => {
1881
+ if (!isVisible) return null;
1882
+ const containerStyle = {
1883
+ ...tooltipPositionStyles[position],
1884
+ ...style
1885
+ };
1886
+ const isFull = variant === PassportVariant$1.Full;
1887
+ return /* @__PURE__ */ jsx("div", {
1888
+ role: "tooltip",
1889
+ style: containerStyle,
1890
+ onMouseEnter,
1891
+ onMouseLeave: onClose,
1892
+ children: /* @__PURE__ */ jsxs(Passport.Root, {
1893
+ validation,
1894
+ style: passportContainerStyles[theme],
1895
+ children: [
1896
+ /* @__PURE__ */ jsxs(Passport.Header, {
1897
+ style: passportHeaderStyles.container,
1898
+ children: [/* @__PURE__ */ jsx(Passport.Logo, { style: passportHeaderStyles.logo }), /* @__PURE__ */ jsx(Passport.Title, { style: passportHeaderStyles.title[theme] })]
1899
+ }),
1900
+ /* @__PURE__ */ jsx(Passport.Field, {
1901
+ name: "issuer",
1902
+ style: fieldContainerStyle,
1903
+ children: ({ label, value }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1904
+ style: fieldLabelStyles[theme],
1905
+ children: label
1906
+ }), /* @__PURE__ */ jsx("div", {
1907
+ style: fieldValueStyles[theme],
1908
+ children: value
1909
+ })] })
1910
+ }),
1911
+ showDate && /* @__PURE__ */ jsx(Passport.Field, {
1912
+ name: "date",
1913
+ style: fieldContainerStyle,
1914
+ children: ({ label, value }) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1915
+ style: fieldLabelStyles[theme],
1916
+ children: label
1917
+ }), /* @__PURE__ */ jsx("div", {
1918
+ style: fieldValueStyles[theme],
1919
+ children: value
1920
+ })] })
1921
+ }),
1922
+ isFull && /* @__PURE__ */ jsx(Passport.Identities, {
1923
+ style: fieldContainerStyle,
1924
+ children: (identities) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1925
+ style: fieldLabelStyles[theme],
1926
+ children: identities.length === 1 ? "Verified Identity" : `Verified Identities (${identities.length})`
1927
+ }), /* @__PURE__ */ jsx("div", {
1928
+ style: identityStyles.container,
1929
+ children: identities.map((identity) => /* @__PURE__ */ jsxs("div", {
1930
+ style: identityStyles.item,
1931
+ children: [identity.provider.toLowerCase().includes("linkedin") && /* @__PURE__ */ jsx(LinkedInIcon, { size: 16 }), identity.uri ? /* @__PURE__ */ jsx("a", {
1932
+ href: identity.uri,
1933
+ target: "_blank",
1934
+ rel: "noopener noreferrer",
1935
+ style: {
1936
+ ...fieldValueStyles[theme],
1937
+ ...identityStyles.link
1938
+ },
1939
+ children: identity.username
1940
+ }) : /* @__PURE__ */ jsx("span", {
1941
+ style: fieldValueStyles[theme],
1942
+ children: identity.username
1943
+ })]
1944
+ }, identity.provider))
1945
+ })] })
1946
+ }),
1947
+ isFull && /* @__PURE__ */ jsx(Passport.Signers, {
1948
+ style: fieldContainerStyle,
1949
+ children: (signers) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1950
+ style: fieldLabelStyles[theme],
1951
+ children: signers.length === 1 ? "Previous Signer" : `Previous Signers (${signers.length})`
1952
+ }), /* @__PURE__ */ jsx("div", {
1953
+ style: signerStyles.container,
1954
+ children: signers.map((signer) => /* @__PURE__ */ jsx("div", {
1955
+ style: fieldValueStyles[theme],
1956
+ children: signer.label
1957
+ }, signer.id))
1958
+ })] })
1959
+ }),
1960
+ isFull && /* @__PURE__ */ jsx(Passport.Actions, {
1961
+ style: fieldContainerStyle,
1962
+ children: (actions) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1963
+ style: fieldLabelStyles[theme],
1964
+ children: "Actions"
1965
+ }), /* @__PURE__ */ jsx("div", {
1966
+ style: actionsContainerStyle,
1967
+ children: actions.map((action) => /* @__PURE__ */ jsx("span", {
1968
+ style: actionTagStyles[theme],
1969
+ children: action
1970
+ }, action))
1971
+ })] })
1972
+ }),
1973
+ /* @__PURE__ */ jsxs(Passport.Footer, {
1974
+ style: passportFooterStyles.container[theme],
1975
+ children: [/* @__PURE__ */ jsx(Passport.CopyButton, { style: passportFooterStyles.copyButton[theme] }), isFull && onClose && /* @__PURE__ */ jsx("button", {
1976
+ type: "button",
1977
+ onClick: onClose,
1978
+ style: passportFooterStyles.closeButton[theme],
1979
+ children: "Close"
1980
+ })]
1981
+ })
1982
+ ]
1983
+ })
1984
+ });
1985
+ };
1986
+ LimboPassport.displayName = "LimboPassport";
1987
+
1988
+ //#endregion
1989
+ //#region src/ui/default/LimboImage.tsx
1990
+ /**
1991
+ * LimboImage - Pre-styled image component with default badge and passport
1992
+ *
1993
+ * A ready-to-use image component that displays content credentials with
1994
+ * a pre-configured badge and passport tooltip.
1995
+ *
1996
+ * @example
1997
+ * ```tsx
1998
+ * // Basic usage with click interaction
1999
+ * <LimboImage
2000
+ * src="/photo.jpg"
2001
+ * alt="Photo"
2002
+ * validation={validation}
2003
+ * validationStatus="success"
2004
+ * />
2005
+ *
2006
+ * // With hover interaction and dark theme
2007
+ * <LimboImage
2008
+ * src="/photo.jpg"
2009
+ * alt="Photo"
2010
+ * validation={validation}
2011
+ * validationStatus="success"
2012
+ * interactionMode="hover"
2013
+ * theme="dark"
2014
+ * badgePosition="bottomRight"
2015
+ * />
2016
+ *
2017
+ * // Full passport variant
2018
+ * <LimboImage
2019
+ * src="/photo.jpg"
2020
+ * alt="Photo"
2021
+ * validation={validation}
2022
+ * validationStatus="success"
2023
+ * passportVariant="full"
2024
+ * />
2025
+ * ```
2026
+ */
2027
+ const LimboImage = forwardRef(({ validation = null, validationStatus = "idle", badgePosition = BadgePosition.TopRight, theme = Theme.Light, passportVariant = PassportVariant$1.Basic, interactionMode = "click", showDate = true, hideDelay = 300, enableHeicConversion = true, wrapperStyle: customWrapperStyle, ...imgProps }, ref) => {
2028
+ const combinedWrapperStyle = {
2029
+ ...wrapperStyle,
2030
+ ...customWrapperStyle
2031
+ };
2032
+ const renderBadge = ({ hasCredentials, toggle, show, hide }) => {
2033
+ if (!hasCredentials) return null;
2034
+ return /* @__PURE__ */ jsx(LimboBadge, {
2035
+ position: badgePosition,
2036
+ theme,
2037
+ ...interactionMode === "hover" ? {
2038
+ onMouseEnter: show,
2039
+ onMouseLeave: hide
2040
+ } : { onClick: toggle }
2041
+ });
2042
+ };
2043
+ const renderTooltip = ({ validation: tooltipValidation, isVisible, onMouseEnter, onClose }) => /* @__PURE__ */ jsx(LimboPassport, {
2044
+ validation: tooltipValidation,
2045
+ isVisible,
2046
+ theme,
2047
+ position: badgePosition,
2048
+ variant: passportVariant,
2049
+ showDate,
2050
+ onMouseEnter: interactionMode === "hover" ? onMouseEnter : void 0,
2051
+ onClose
2052
+ });
2053
+ return /* @__PURE__ */ jsx(MediaImage, {
2054
+ ref,
2055
+ ...imgProps,
2056
+ validation,
2057
+ validationStatus,
2058
+ hideDelay,
2059
+ enableHeicConversion,
2060
+ wrapperStyle: combinedWrapperStyle,
2061
+ renderBadge,
2062
+ renderTooltip
2063
+ });
2064
+ });
2065
+ LimboImage.displayName = "LimboImage";
2066
+
2067
+ //#endregion
2068
+ //#region src/ui/default/LimboVideo.tsx
2069
+ /**
2070
+ * LimboVideo - Pre-styled video component with default badge and passport
2071
+ *
2072
+ * A ready-to-use video component that displays content credentials with
2073
+ * a pre-configured badge and passport tooltip.
2074
+ *
2075
+ * @example
2076
+ * ```tsx
2077
+ * // Basic usage with click interaction
2078
+ * <LimboVideo
2079
+ * src="/video.mp4"
2080
+ * controls
2081
+ * validation={validation}
2082
+ * validationStatus="success"
2083
+ * />
2084
+ *
2085
+ * // With hover interaction and dark theme
2086
+ * <LimboVideo
2087
+ * src="/video.mp4"
2088
+ * controls
2089
+ * validation={validation}
2090
+ * validationStatus="success"
2091
+ * interactionMode="hover"
2092
+ * theme="dark"
2093
+ * badgePosition="bottomRight"
2094
+ * />
2095
+ *
2096
+ * // Full passport variant
2097
+ * <LimboVideo
2098
+ * src="/video.mp4"
2099
+ * controls
2100
+ * validation={validation}
2101
+ * validationStatus="success"
2102
+ * passportVariant="full"
2103
+ * />
2104
+ * ```
2105
+ */
2106
+ const LimboVideo = forwardRef(({ validation = null, validationStatus = "idle", badgePosition = BadgePosition.TopRight, theme = Theme.Light, passportVariant = PassportVariant$1.Basic, interactionMode = "click", showDate = true, hideDelay = 300, wrapperStyle: customWrapperStyle, ...videoProps }, ref) => {
2107
+ const combinedWrapperStyle = {
2108
+ ...wrapperStyle,
2109
+ ...customWrapperStyle
2110
+ };
2111
+ const renderBadge = ({ hasCredentials, toggle, show, hide }) => {
2112
+ if (!hasCredentials) return null;
2113
+ return /* @__PURE__ */ jsx(LimboBadge, {
2114
+ position: badgePosition,
2115
+ theme,
2116
+ ...interactionMode === "hover" ? {
2117
+ onMouseEnter: show,
2118
+ onMouseLeave: hide
2119
+ } : { onClick: toggle }
2120
+ });
2121
+ };
2122
+ const renderTooltip = ({ validation: tooltipValidation, isVisible, onMouseEnter, onClose }) => /* @__PURE__ */ jsx(LimboPassport, {
2123
+ validation: tooltipValidation,
2124
+ isVisible,
2125
+ theme,
2126
+ position: badgePosition,
2127
+ variant: passportVariant,
2128
+ showDate,
2129
+ onMouseEnter: interactionMode === "hover" ? onMouseEnter : void 0,
2130
+ onClose
2131
+ });
2132
+ return /* @__PURE__ */ jsx(MediaVideo, {
2133
+ ref,
2134
+ ...videoProps,
2135
+ validation,
2136
+ validationStatus,
2137
+ hideDelay,
2138
+ wrapperStyle: combinedWrapperStyle,
2139
+ renderBadge,
2140
+ renderTooltip
2141
+ });
2142
+ });
2143
+ LimboVideo.displayName = "LimboVideo";
2144
+
2145
+ //#endregion
2146
+ export { BadgePosition, CRLogo, CredentialBadge, CredentialOverlay, LimboBadge, LimboImage, LimboPassport, LimboVideo, LinkedInIcon, MediaImage, MediaVideo, Passport, PassportVariant, Theme, ValidationStatus, useCredentialBadgeContext, useCredentialOverlayContext, useHeicConversion, useMediaContext, usePassportContext, usePassportData, useTooltipVisibility };
2147
+ //# sourceMappingURL=index.mjs.map