@washi-ui/react 1.0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useWashi.ts","../src/WashiProvider.tsx","../src/WashiFrame.tsx","../src/CommentList.tsx","../src/WashiToolBubble.tsx","../src/WashiCommentsSidebar.tsx","../src/WashiPinDialog.tsx","../src/WashiUI.tsx"],"sourcesContent":["import { useRef, useState, useEffect, useCallback } from 'react';\nimport {\n Washi,\n WashiAdapter,\n WashiMode,\n Comment,\n NewComment,\n MountOptions,\n PinPlacedEvent,\n} from '@washi-ui/core';\n\n/**\n * Options for the useWashi hook\n */\nexport interface UseWashiOptions {\n /** Required: The storage adapter for persisting comments */\n adapter: WashiAdapter;\n /** Optional: Initial mode ('view' or 'annotate'). Defaults to 'view' */\n initialMode?: WashiMode;\n /** Optional: Mount options for the Washi instance */\n mountOptions?: MountOptions;\n /** Optional: Callback when a pin is placed on the overlay in annotate mode */\n onPinPlaced?: (event: PinPlacedEvent) => void;\n /** Optional: Callback when a pin is clicked */\n onCommentClick?: (comment: Comment) => void;\n /** Optional: Callback when a comment is updated */\n onCommentUpdate?: (data: { id: string; updates: Partial<Comment> }) => void;\n /** Optional: Callback when a comment is deleted */\n onCommentDelete?: (id: string) => void;\n}\n\n/**\n * Return type of the useWashi hook\n */\nexport interface UseWashiReturn {\n /** Ref to attach to the iframe element */\n iframeRef: React.RefObject<HTMLIFrameElement | null>;\n /** Current mode */\n mode: WashiMode;\n /** Function to change the mode */\n setMode: (mode: WashiMode) => void;\n /** Array of all comments */\n comments: Comment[];\n /** Add a new comment. Returns the completed Comment with generated id and createdAt. */\n addComment: (input: NewComment) => Promise<Comment>;\n /** Update an existing comment */\n updateComment: (id: string, updates: Partial<Comment>) => Promise<void>;\n /** Delete a comment */\n deleteComment: (id: string) => Promise<void>;\n /** Whether the Washi instance is mounted and ready */\n isReady: boolean;\n /** Any error that occurred during mounting or operations */\n error: Error | null;\n}\n\n/**\n * React hook for using Washi comment system.\n *\n * @param options - Configuration options\n * @returns Object with iframe ref, state, and methods\n *\n * @example\n * ```tsx\n * function CommentableContent() {\n * const {\n * iframeRef,\n * mode,\n * setMode,\n * comments,\n * addComment,\n * isReady\n * } = useWashi({\n * adapter: new MyAdapter(),\n * onPinPlaced: ({ x, y }) => {\n * // Show comment input dialog at position\n * },\n * onCommentClick: (comment) => {\n * // Show comment details\n * }\n * });\n *\n * return (\n * <div>\n * <button onClick={() => setMode(mode === 'view' ? 'annotate' : 'view')}>\n * Toggle Mode\n * </button>\n * <div style={{ position: 'relative' }}>\n * <iframe ref={iframeRef} src=\"/content.html\" />\n * </div>\n * </div>\n * );\n * }\n * ```\n */\nexport function useWashi(options: UseWashiOptions): UseWashiReturn {\n const {\n adapter,\n initialMode = 'view',\n mountOptions,\n onPinPlaced,\n onCommentClick,\n onCommentUpdate,\n onCommentDelete,\n } = options;\n\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n const washiRef = useRef<Washi | null>(null);\n\n const [mode, setModeState] = useState<WashiMode>(initialMode);\n const [comments, setComments] = useState<Comment[]>([]);\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Initialize Washi instance\n useEffect(() => {\n washiRef.current = new Washi(adapter);\n return () => {\n washiRef.current?.unmount();\n washiRef.current = null;\n };\n }, [adapter]);\n\n // Mount to iframe when available\n useEffect(() => {\n const iframe = iframeRef.current;\n const washi = washiRef.current;\n\n if (!iframe || !washi) return;\n\n const handleLoad = async () => {\n try {\n await washi.mount(iframe, mountOptions);\n setComments(washi.getComments());\n if (initialMode !== 'view') {\n washi.setMode(initialMode);\n }\n setIsReady(true);\n setError(null);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsReady(false);\n }\n };\n\n // If iframe is already loaded, mount immediately\n if (iframe.contentDocument?.readyState === 'complete') {\n handleLoad();\n } else {\n iframe.addEventListener('load', handleLoad);\n }\n\n return () => {\n iframe.removeEventListener('load', handleLoad);\n };\n }, [mountOptions, initialMode]);\n\n // Subscribe to events\n useEffect(() => {\n const washi = washiRef.current;\n if (!washi || !isReady) return;\n\n const unsubscribers: (() => void)[] = [];\n\n if (onPinPlaced) {\n unsubscribers.push(\n washi.on('pin:placed', (event: PinPlacedEvent) => {\n onPinPlaced(event);\n }),\n );\n }\n\n if (onCommentClick) {\n unsubscribers.push(\n washi.on('comment:clicked', (comment: Comment) => {\n onCommentClick(comment);\n }),\n );\n }\n\n if (onCommentUpdate) {\n unsubscribers.push(\n washi.on(\n 'comment:updated',\n (data: { id: string; updates: Partial<Comment> }) => {\n onCommentUpdate(data);\n },\n ),\n );\n }\n\n if (onCommentDelete) {\n unsubscribers.push(\n washi.on('comment:deleted', (id: string) => {\n onCommentDelete(id);\n }),\n );\n }\n\n return () => {\n unsubscribers.forEach((unsub) => unsub());\n };\n }, [isReady, onPinPlaced, onCommentClick, onCommentUpdate, onCommentDelete]);\n\n const setMode = useCallback((newMode: WashiMode) => {\n const washi = washiRef.current;\n if (!washi) return;\n\n try {\n washi.setMode(newMode);\n setModeState(newMode);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n }, []);\n\n const addComment = useCallback(async (input: NewComment): Promise<Comment> => {\n const washi = washiRef.current;\n if (!washi) throw new Error('Washi is not initialized');\n\n const comment = await washi.addComment(input);\n setComments(washi.getComments());\n return comment;\n }, []);\n\n const updateComment = useCallback(\n async (id: string, updates: Partial<Comment>) => {\n const washi = washiRef.current;\n if (!washi) throw new Error('Washi is not initialized');\n\n await washi.updateComment(id, updates);\n setComments(washi.getComments());\n },\n [],\n );\n\n const deleteComment = useCallback(async (id: string) => {\n const washi = washiRef.current;\n if (!washi) throw new Error('Washi is not initialized');\n\n await washi.deleteComment(id);\n setComments(washi.getComments());\n }, []);\n\n return {\n iframeRef,\n mode,\n setMode,\n comments,\n addComment,\n updateComment,\n deleteComment,\n isReady,\n error,\n };\n}\n","import {\n createContext,\n useContext,\n useRef,\n useState,\n useEffect,\n useCallback,\n useMemo,\n ReactNode,\n} from 'react';\nimport {\n Washi,\n WashiAdapter,\n WashiMode,\n Comment,\n NewComment,\n MountOptions,\n PinPlacedEvent,\n} from '@washi-ui/core';\n\n/**\n * Context value provided by WashiProvider\n */\nexport interface WashiContextValue {\n /** The Washi instance (null if not mounted) */\n washi: Washi | null;\n /** The registered iframe element (null if not mounted) */\n iframeEl: HTMLIFrameElement | null;\n /** Current mode */\n mode: WashiMode;\n /** Function to change the mode */\n setMode: (mode: WashiMode) => void;\n /** Array of all comments */\n comments: Comment[];\n /** Add a new comment. Returns the completed Comment with generated id and createdAt. */\n addComment: (input: NewComment) => Promise<Comment>;\n /** Update an existing comment */\n updateComment: (id: string, updates: Partial<Comment>) => Promise<void>;\n /** Delete a comment */\n deleteComment: (id: string) => Promise<void>;\n /** Refresh comments from adapter */\n refreshComments: () => void;\n /** Whether the Washi instance is mounted and ready */\n isReady: boolean;\n /** Any error that occurred */\n error: Error | null;\n /** Register an iframe element for mounting */\n registerIframe: (iframe: HTMLIFrameElement | null) => void;\n /** Subscribe to pin placement events (overlay clicked in annotate mode) */\n onPinPlaced: (callback: (event: PinPlacedEvent) => void) => () => void;\n /** Subscribe to comment click events */\n onCommentClick: (callback: (comment: Comment) => void) => () => void;\n /** Set the active pin for highlighting */\n setActivePin: (commentId: string | null) => void;\n /** Get the index of a comment (sorted by createdAt) */\n getCommentIndex: (commentId: string) => number;\n /** Currently selected/active comment (for custom dialog rendering) */\n activeComment: Comment | null;\n /** Set the active comment (for custom dialog rendering) */\n setActiveComment: (comment: Comment | null) => void;\n}\n\nconst WashiContext = createContext<WashiContextValue | null>(null);\n\n/**\n * Props for WashiProvider\n */\nexport interface WashiProviderProps {\n /** Required: The storage adapter for persisting comments */\n adapter: WashiAdapter;\n /** Optional: Initial mode ('view' or 'annotate'). Defaults to 'view' */\n initialMode?: WashiMode;\n /** Optional: Mount options for the Washi instance */\n mountOptions?: MountOptions;\n /** Child components */\n children: ReactNode;\n}\n\n/**\n * Provider component for Washi context.\n * Use with WashiFrame and useWashiContext.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <WashiProvider adapter={new MyAdapter()}>\n * <WashiFrame src=\"/content.html\" />\n * <CommentSidebar />\n * </WashiProvider>\n * );\n * }\n * ```\n */\nexport function WashiProvider({\n adapter,\n initialMode = 'view',\n mountOptions,\n children,\n}: WashiProviderProps) {\n // Create Washi instance synchronously to avoid race conditions\n const washiRef = useRef<Washi | null>(null);\n if (!washiRef.current) {\n washiRef.current = new Washi(adapter);\n }\n\n const iframeRef = useRef<HTMLIFrameElement | null>(null);\n // Stores cleanup for the current iframe's 'load' listener so it can be\n // removed when registerIframe(null) is called (required for React StrictMode).\n const iframeLoadCleanupRef = useRef<(() => void) | null>(null);\n // Stores unsubscribe functions for Washi event listeners so they can be\n // removed before each remount cycle.\n const washiEventCleanupsRef = useRef<Array<() => void>>([]);\n\n const pinPlacedCallbacksRef = useRef<Set<(event: PinPlacedEvent) => void>>(\n new Set(),\n );\n const clickCallbacksRef = useRef<Set<(comment: Comment) => void>>(new Set());\n\n const [mode, setModeState] = useState<WashiMode>(initialMode);\n const [comments, setComments] = useState<Comment[]>([]);\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [activeComment, setActiveCommentState] = useState<Comment | null>(null);\n const [iframeElState, setIframeElState] = useState<HTMLIFrameElement | null>(null);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n washiRef.current?.unmount();\n };\n }, []);\n\n const refreshComments = useCallback(() => {\n const washi = washiRef.current;\n if (washi) {\n setComments(washi.getComments());\n }\n }, []);\n\n const registerIframe = useCallback(\n (iframe: HTMLIFrameElement | null): void => {\n // Always tear down the previous load listener and Washi event subscriptions.\n // This makes the effect symmetric so React StrictMode's setup→cleanup→setup\n // cycle works correctly (each setup gets a clean slate).\n iframeLoadCleanupRef.current?.();\n iframeLoadCleanupRef.current = null;\n\n washiEventCleanupsRef.current.forEach((fn) => fn());\n washiEventCleanupsRef.current = [];\n\n iframeRef.current = iframe;\n setIframeElState(iframe);\n\n if (!iframe) {\n // Unmount Washi so the next registerIframe(iframe) call can re-mount cleanly.\n if (washiRef.current) {\n washiRef.current.unmount();\n setIsReady(false);\n setComments([]);\n }\n return;\n }\n\n if (!washiRef.current) return;\n\n const washi = washiRef.current;\n\n const handleLoad = async () => {\n try {\n await washi.mount(iframe, mountOptions);\n\n // Bail out if this mount cycle was already cancelled (registerIframe(null)\n // was called while we were awaiting — e.g. StrictMode cleanup).\n if (iframeRef.current !== iframe) return;\n\n setComments(washi.getComments());\n if (initialMode !== 'view') {\n washi.setMode(initialMode);\n }\n setIsReady(true);\n setError(null);\n\n // Subscribe to events — store unsubscribes for cleanup\n washiEventCleanupsRef.current.push(\n washi.on('pin:placed', (event: PinPlacedEvent) => {\n pinPlacedCallbacksRef.current.forEach((cb) => cb(event));\n }),\n );\n\n washiEventCleanupsRef.current.push(\n washi.on('comment:clicked', (comment: Comment) => {\n clickCallbacksRef.current.forEach((cb) => cb(comment));\n }),\n );\n\n washiEventCleanupsRef.current.push(\n washi.on('comment:updated', () => {\n setComments(washi.getComments());\n }),\n );\n\n washiEventCleanupsRef.current.push(\n washi.on('comment:deleted', () => {\n setComments(washi.getComments());\n }),\n );\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsReady(false);\n }\n };\n\n if (iframe.contentDocument?.readyState === 'complete') {\n handleLoad();\n } else {\n iframe.addEventListener('load', handleLoad);\n iframeLoadCleanupRef.current = () =>\n iframe.removeEventListener('load', handleLoad);\n }\n },\n [mountOptions, initialMode],\n );\n\n const setMode = useCallback((newMode: WashiMode) => {\n const washi = washiRef.current;\n if (!washi) return;\n\n try {\n washi.setMode(newMode);\n setModeState(newMode);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n }\n }, []);\n\n const addComment = useCallback(async (input: NewComment): Promise<Comment> => {\n const washi = washiRef.current;\n if (!washi) throw new Error('Washi is not initialized');\n\n const comment = await washi.addComment(input);\n setComments(washi.getComments());\n return comment;\n }, []);\n\n const updateComment = useCallback(\n async (id: string, updates: Partial<Comment>) => {\n const washi = washiRef.current;\n if (!washi) throw new Error('Washi is not initialized');\n\n await washi.updateComment(id, updates);\n setComments(washi.getComments());\n },\n [],\n );\n\n const deleteComment = useCallback(async (id: string) => {\n const washi = washiRef.current;\n if (!washi) throw new Error('Washi is not initialized');\n\n await washi.deleteComment(id);\n setComments(washi.getComments());\n }, []);\n\n const onPinPlaced = useCallback(\n (callback: (event: PinPlacedEvent) => void) => {\n pinPlacedCallbacksRef.current.add(callback);\n return () => {\n pinPlacedCallbacksRef.current.delete(callback);\n };\n },\n [],\n );\n\n const onCommentClick = useCallback(\n (callback: (comment: Comment) => void) => {\n clickCallbacksRef.current.add(callback);\n return () => {\n clickCallbacksRef.current.delete(callback);\n };\n },\n [],\n );\n\n const setActivePin = useCallback((commentId: string | null) => {\n const washi = washiRef.current;\n if (washi) {\n washi.setActivePin(commentId);\n }\n }, []);\n\n const getCommentIndex = useCallback((commentId: string) => {\n const washi = washiRef.current;\n if (washi) {\n return washi.getCommentIndex(commentId);\n }\n return -1;\n }, []);\n\n const setActiveComment = useCallback((comment: Comment | null) => {\n setActiveCommentState(comment);\n const washi = washiRef.current;\n if (washi) {\n washi.setActivePin(comment?.id ?? null);\n }\n }, []);\n\n const value = useMemo<WashiContextValue>(\n () => ({\n washi: washiRef.current,\n iframeEl: iframeElState,\n mode,\n setMode,\n comments,\n addComment,\n updateComment,\n deleteComment,\n refreshComments,\n isReady,\n error,\n registerIframe,\n onPinPlaced,\n onCommentClick,\n setActivePin,\n getCommentIndex,\n activeComment,\n setActiveComment,\n }),\n [\n iframeElState,\n mode,\n setMode,\n comments,\n addComment,\n updateComment,\n deleteComment,\n refreshComments,\n isReady,\n error,\n registerIframe,\n onPinPlaced,\n onCommentClick,\n setActivePin,\n getCommentIndex,\n activeComment,\n setActiveComment,\n ],\n );\n\n return (\n <WashiContext.Provider value={value}>{children}</WashiContext.Provider>\n );\n}\n\n/**\n * Hook to access Washi context.\n * Must be used within a WashiProvider.\n *\n * @throws Error if used outside of WashiProvider\n *\n * @example\n * ```tsx\n * function CommentSidebar() {\n * const { comments, deleteComment } = useWashiContext();\n *\n * return (\n * <ul>\n * {comments.map(c => (\n * <li key={c.id}>\n * {c.text}\n * <button onClick={() => deleteComment(c.id)}>Delete</button>\n * </li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useWashiContext(): WashiContextValue {\n const context = useContext(WashiContext);\n if (!context) {\n throw new Error('useWashiContext must be used within a WashiProvider');\n }\n return context;\n}\n","import React, { useEffect, useRef, IframeHTMLAttributes } from 'react';\nimport { useWashiContext } from './WashiProvider';\n\n/**\n * Props for WashiFrame component\n */\nexport interface WashiFrameProps\n extends Omit<IframeHTMLAttributes<HTMLIFrameElement>, 'ref'> {\n /** URL to load in the iframe. Omit when using srcDoc to pass an HTML string. */\n src?: string;\n /** Optional: Additional CSS class name */\n className?: string;\n /** Optional: Inline styles */\n style?: React.CSSProperties;\n}\n\n/**\n * Iframe wrapper component that automatically registers with WashiProvider.\n * Must be used within a WashiProvider.\n *\n * @example\n * ```tsx\n * <WashiProvider adapter={adapter}>\n * <WashiFrame\n * src=\"/content.html\"\n * style={{ width: '100%', height: '600px', border: 'none' }}\n * />\n * </WashiProvider>\n * ```\n */\nexport function WashiFrame({ src, className, style, ...props }: WashiFrameProps) {\n const { registerIframe } = useWashiContext();\n const iframeRef = useRef<HTMLIFrameElement>(null);\n\n useEffect(() => {\n if (iframeRef.current) {\n registerIframe(iframeRef.current);\n }\n\n return () => {\n registerIframe(null);\n };\n }, [registerIframe]);\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={style}\n {...props}\n />\n );\n}\n","import React, { ReactNode } from 'react';\nimport { Comment } from '@washi-ui/core';\nimport { useWashiContext } from './WashiProvider';\n\n/**\n * Props for CommentList component\n */\nexport interface CommentListProps {\n /**\n * Render function for each comment.\n * Receives the comment and action handlers.\n */\n renderComment: (\n comment: Comment,\n actions: {\n onResolve: () => Promise<void>;\n onDelete: () => Promise<void>;\n onUpdate: (updates: Partial<Comment>) => Promise<void>;\n },\n ) => ReactNode;\n /**\n * Optional: Filter function to show only certain comments\n */\n filter?: (comment: Comment) => boolean;\n /**\n * Optional: Sort function for comments\n */\n sort?: (a: Comment, b: Comment) => number;\n /**\n * Optional: Component to render when there are no comments\n */\n emptyState?: ReactNode;\n /**\n * Optional: CSS class name for the list container\n */\n className?: string;\n /**\n * Optional: Inline styles for the list container\n */\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component for rendering a list of comments.\n * Must be used within a WashiProvider.\n *\n * @example\n * ```tsx\n * <CommentList\n * renderComment={(comment, { onResolve, onDelete }) => (\n * <div key={comment.id} className=\"comment-item\">\n * <p>{comment.text}</p>\n * <div className=\"actions\">\n * <button onClick={onResolve}>\n * {comment.resolved ? 'Unresolve' : 'Resolve'}\n * </button>\n * <button onClick={onDelete}>Delete</button>\n * </div>\n * </div>\n * )}\n * filter={(c) => !c.resolved}\n * sort={(a, b) => b.createdAt - a.createdAt}\n * emptyState={<p>No comments yet</p>}\n * />\n * ```\n */\nexport function CommentList({\n renderComment,\n filter,\n sort,\n emptyState,\n className,\n style,\n}: CommentListProps) {\n const { comments, updateComment, deleteComment } = useWashiContext();\n\n let displayComments = [...comments];\n\n if (filter) {\n displayComments = displayComments.filter(filter);\n }\n\n if (sort) {\n displayComments.sort(sort);\n }\n\n if (displayComments.length === 0 && emptyState) {\n return <>{emptyState}</>;\n }\n\n return (\n <div className={className} style={style}>\n {displayComments.map((comment) => {\n const actions = {\n onResolve: async () => {\n await updateComment(comment.id, { resolved: !comment.resolved });\n },\n onDelete: async () => {\n await deleteComment(comment.id);\n },\n onUpdate: async (updates: Partial<Comment>) => {\n await updateComment(comment.id, updates);\n },\n };\n\n return (\n <React.Fragment key={comment.id}>\n {renderComment(comment, actions)}\n </React.Fragment>\n );\n })}\n </div>\n );\n}\n","import { useState } from 'react';\nimport { useWashiContext } from './WashiProvider';\n\nexport type WashiToolBubblePosition =\n | 'bottom-right'\n | 'bottom-left'\n | 'top-right'\n | 'top-left';\n\nexport interface WashiToolBubbleProps {\n position?: WashiToolBubblePosition;\n sidebarOpen?: boolean;\n onSidebarToggle?: () => void;\n accentColor?: string;\n}\n\nconst positionStyles: Record<WashiToolBubblePosition, React.CSSProperties> = {\n 'bottom-right': { bottom: 24, right: 24 },\n 'bottom-left': { bottom: 24, left: 24 },\n 'top-right': { top: 24, right: 24 },\n 'top-left': { top: 24, left: 24 },\n};\n\n// Pencil icon SVG\nfunction PencilIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\" />\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\" />\n </svg>\n );\n}\n\n// Chat bubble icon SVG\nfunction ChatIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n );\n}\n\nexport function WashiToolBubble({\n position = 'bottom-right',\n sidebarOpen = false,\n onSidebarToggle,\n accentColor = '#667eea',\n}: WashiToolBubbleProps) {\n const { mode, setMode, isReady, comments } = useWashiContext();\n const [hoveredBtn, setHoveredBtn] = useState<'pencil' | 'chat' | null>(null);\n\n const isAnnotate = mode === 'annotate';\n const commentCount = comments.length;\n const badgeLabel = commentCount > 99 ? '99+' : String(commentCount);\n\n const pillStyle: React.CSSProperties = {\n position: 'fixed',\n ...positionStyles[position],\n zIndex: 9998,\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n padding: '6px',\n borderRadius: '999px',\n backgroundColor: 'rgba(255,255,255,0.92)',\n backdropFilter: 'blur(8px)',\n WebkitBackdropFilter: 'blur(8px)',\n boxShadow: '0 4px 16px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.08)',\n border: '1px solid rgba(0,0,0,0.06)',\n };\n\n const btnBase: React.CSSProperties = {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 40,\n height: 40,\n borderRadius: '50%',\n border: 'none',\n cursor: 'pointer',\n transition: 'background-color 0.15s, box-shadow 0.15s, color 0.15s',\n outline: 'none',\n };\n\n const pencilActive = isAnnotate;\n const pencilDisabled = !isReady;\n\n const pencilStyle: React.CSSProperties = {\n ...btnBase,\n backgroundColor: pencilActive\n ? accentColor\n : hoveredBtn === 'pencil'\n ? 'rgba(0,0,0,0.06)'\n : 'transparent',\n color: pencilActive ? '#fff' : '#374151',\n opacity: pencilDisabled ? 0.5 : 1,\n cursor: pencilDisabled ? 'not-allowed' : 'pointer',\n boxShadow: pencilActive\n ? `0 0 0 3px ${accentColor}33`\n : 'none',\n };\n\n const chatStyle: React.CSSProperties = {\n ...btnBase,\n backgroundColor: sidebarOpen\n ? 'rgba(0,0,0,0.06)'\n : hoveredBtn === 'chat'\n ? 'rgba(0,0,0,0.06)'\n : 'transparent',\n color: '#374151',\n };\n\n const badgeStyle: React.CSSProperties = {\n position: 'absolute',\n top: 4,\n right: 4,\n minWidth: 16,\n height: 16,\n borderRadius: '999px',\n backgroundColor: accentColor,\n color: '#fff',\n fontSize: '10px',\n fontWeight: 700,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '0 3px',\n lineHeight: 1,\n pointerEvents: 'none',\n };\n\n return (\n <div style={pillStyle}>\n <button\n style={pencilStyle}\n disabled={pencilDisabled}\n title={isAnnotate ? 'Exit annotate mode' : 'Enter annotate mode'}\n onClick={() => !pencilDisabled && setMode(isAnnotate ? 'view' : 'annotate')}\n onMouseEnter={() => setHoveredBtn('pencil')}\n onMouseLeave={() => setHoveredBtn(null)}\n >\n <PencilIcon />\n </button>\n <button\n style={chatStyle}\n title={sidebarOpen ? 'Close comments' : 'Open comments'}\n onClick={onSidebarToggle}\n onMouseEnter={() => setHoveredBtn('chat')}\n onMouseLeave={() => setHoveredBtn(null)}\n >\n <ChatIcon />\n {commentCount > 0 && (\n <span style={badgeStyle}>{badgeLabel}</span>\n )}\n </button>\n </div>\n );\n}\n","import { useState, useEffect } from 'react';\nimport { useWashiContext } from './WashiProvider';\nimport type { WashiToolBubblePosition } from './WashiToolBubble';\n\nexport interface WashiCommentsSidebarProps {\n open: boolean;\n onClose: () => void;\n /** Which corner the tool bubble is anchored to. Panel slides in from that side and stops above the bubble. Default: 'bottom-right' */\n position?: WashiToolBubblePosition;\n accentColor?: string;\n}\n\n// Must match WashiToolBubble layout constants\nconst BUBBLE_EDGE = 24; // px from screen edge\nconst BUBBLE_HEIGHT = 52; // pill height: 6px pad + 40px button + 6px pad\nconst GAP = 10; // breathing room between panel bottom and bubble top\n\n/** Returns the fixed-position bounds that leave the tool bubble area exposed */\nfunction getPanelBounds(position: WashiToolBubblePosition): React.CSSProperties {\n const reserved = BUBBLE_EDGE + BUBBLE_HEIGHT + GAP; // ~86px\n switch (position) {\n case 'bottom-right': return { top: 0, right: 0, bottom: reserved };\n case 'bottom-left': return { top: 0, left: 0, bottom: reserved };\n case 'top-right': return { top: reserved, right: 0, bottom: 0 };\n case 'top-left': return { top: reserved, left: 0, bottom: 0 };\n }\n}\n\n// Inject slide keyframes once at module load\nconst KEYFRAMES_ID = '__washi-panel-kf__';\nif (typeof document !== 'undefined' && !document.getElementById(KEYFRAMES_ID)) {\n const style = document.createElement('style');\n style.id = KEYFRAMES_ID;\n style.textContent = `\n @keyframes washi-panel-in-right { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }\n @keyframes washi-panel-out-right { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }\n @keyframes washi-panel-in-left { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }\n @keyframes washi-panel-out-left { from { transform: translateX(0); opacity: 1; } to { transform: translateX(-100%); opacity: 0; } }\n `;\n document.head.appendChild(style);\n}\n\nconst ANIM_DURATION = 240;\n\nexport function WashiCommentsSidebar({\n open,\n onClose,\n position = 'bottom-right',\n accentColor = '#667eea',\n}: WashiCommentsSidebarProps) {\n const { comments, updateComment, deleteComment } = useWashiContext();\n\n const [visible, setVisible] = useState(open);\n const [phase, setPhase] = useState<'in' | 'out'>(open ? 'in' : 'out');\n\n useEffect(() => {\n if (open) {\n setVisible(true);\n const raf = requestAnimationFrame(() => setPhase('in'));\n return () => cancelAnimationFrame(raf);\n } else {\n setPhase('out');\n const t = setTimeout(() => setVisible(false), ANIM_DURATION);\n return () => clearTimeout(t);\n }\n }, [open]);\n\n if (!visible) return null;\n\n const sortedComments = [...comments].sort((a, b) => a.createdAt - b.createdAt);\n const side = position.endsWith('right') ? 'right' : 'left';\n const animName = phase === 'in' ? `washi-panel-in-${side}` : `washi-panel-out-${side}`;\n\n const panelStyle: React.CSSProperties = {\n position: 'fixed',\n ...getPanelBounds(position),\n width: 320,\n zIndex: 9999,\n display: 'flex',\n flexDirection: 'column',\n backgroundColor: '#f9fafb',\n boxShadow: side === 'right'\n ? '-4px 0 24px rgba(0,0,0,0.10)'\n : '4px 0 24px rgba(0,0,0,0.10)',\n borderLeft: side === 'right' ? '1px solid #e5e7eb' : 'none',\n borderRight: side === 'left' ? '1px solid #e5e7eb' : 'none',\n animation: `${animName} ${ANIM_DURATION}ms cubic-bezier(0.4,0,0.2,1) forwards`,\n fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\",\n };\n\n return (\n <div style={panelStyle}>\n {/* Header */}\n <div style={{\n display: 'flex', alignItems: 'center', justifyContent: 'space-between',\n padding: '16px 20px', borderBottom: '1px solid #e5e7eb',\n backgroundColor: '#fff', flexShrink: 0,\n }}>\n <span style={{ fontSize: '1rem', fontWeight: 600, color: '#1f2937' }}>\n Comments ({comments.length})\n </span>\n <button\n onClick={onClose}\n title=\"Close\"\n style={{\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n width: 30, height: 30, border: 'none', borderRadius: 6,\n backgroundColor: 'transparent', cursor: 'pointer',\n color: '#9ca3af', fontSize: 14, padding: 0,\n }}\n >\n ✕\n </button>\n </div>\n\n {/* Comment list */}\n <div style={{ flex: 1, overflowY: 'auto', padding: '12px' }}>\n {sortedComments.length === 0 ? (\n <div style={{\n textAlign: 'center', padding: '40px 20px',\n color: '#9ca3af', fontSize: '0.875rem',\n }}>\n <p style={{ marginBottom: 8 }}>No comments yet.</p>\n <p>Use the annotate tool to add one.</p>\n </div>\n ) : (\n sortedComments.map((comment, index) => {\n const color = comment.color || accentColor;\n const borderColor = comment.resolved ? '#10b981' : color;\n const badgeBg = comment.resolved ? '#10b981' : color;\n\n return (\n <div\n key={comment.id}\n style={{\n backgroundColor: '#fff',\n border: '1px solid #e5e7eb',\n borderLeft: `3px solid ${borderColor}`,\n borderRadius: 8,\n padding: '12px',\n marginBottom: 10,\n opacity: comment.resolved ? 0.7 : 1,\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 8 }}>\n <div style={{ display: 'flex', alignItems: 'center' }}>\n <span style={{\n display: 'inline-flex', alignItems: 'center', justifyContent: 'center',\n width: 20, height: 20, borderRadius: '50%', backgroundColor: badgeBg,\n color: '#fff', fontSize: '0.7rem', fontWeight: 700, marginRight: 8, flexShrink: 0,\n }}>\n {comment.resolved ? '✓' : index + 1}\n </span>\n <span style={{ fontSize: '0.75rem', color: '#6b7280' }}>\n ({comment.x.toFixed(1)}%, {comment.y.toFixed(1)}%)\n </span>\n </div>\n {comment.resolved && (\n <span style={{ fontSize: '0.75rem', color: '#059669', fontWeight: 500 }}>\n Resolved\n </span>\n )}\n </div>\n <p style={{ fontSize: '0.875rem', color: '#374151', marginBottom: 10, lineHeight: 1.5 }}>\n {comment.text}\n </p>\n <div style={{ display: 'flex', gap: 8 }}>\n <button\n style={{\n padding: '4px 10px', fontSize: '0.75rem', border: 'none',\n borderRadius: 4, cursor: 'pointer', backgroundColor: '#e0e7ff', color: '#4f46e5',\n }}\n onClick={() => updateComment(comment.id, { resolved: !comment.resolved })}\n >\n {comment.resolved ? 'Unresolve' : 'Resolve'}\n </button>\n <button\n style={{\n padding: '4px 10px', fontSize: '0.75rem', border: 'none',\n borderRadius: 4, cursor: 'pointer', backgroundColor: '#fee2e2', color: '#dc2626',\n }}\n onClick={() => deleteComment(comment.id)}\n >\n Delete\n </button>\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n}\n","import { useState, useEffect } from 'react';\nimport { useWashiContext } from './WashiProvider';\nimport type { Comment, PinPlacedEvent } from '@washi-ui/core';\n\nexport interface WashiPinDialogProps {\n accentColor?: string;\n /** Optional callback fired after a comment is successfully created */\n onComment?: (comment: Comment) => void;\n}\n\ninterface PendingPin {\n x: number;\n y: number;\n pixelX: number;\n pixelY: number;\n containerW: number;\n containerH: number;\n}\n\n/**\n * Self-contained pin comment dialog.\n *\n * Subscribes to `pin:placed` events from the context and renders a small\n * popover anchored to the clicked position. On submit it calls `addComment`\n * and switches the mode back to 'view' automatically.\n *\n * Drop this anywhere inside a `<WashiProvider>` — no props required.\n */\nexport function WashiPinDialog({ accentColor = '#667eea', onComment }: WashiPinDialogProps) {\n const { onPinPlaced, addComment, setMode, iframeEl } = useWashiContext();\n const [pending, setPending] = useState<PendingPin | null>(null);\n const [text, setText] = useState('');\n\n useEffect(() => {\n return onPinPlaced((event: PinPlacedEvent) => {\n const containerW = iframeEl?.clientWidth ?? window.innerWidth;\n const containerH = iframeEl?.clientHeight ?? window.innerHeight;\n setText('');\n setPending({\n x: event.x,\n y: event.y,\n pixelX: (event.x / 100) * containerW,\n pixelY: (event.y / 100) * containerH,\n containerW,\n containerH,\n });\n });\n }, [onPinPlaced, iframeEl]);\n\n if (!pending) return null;\n\n const handleSubmit = async () => {\n if (!text.trim()) return;\n const comment = await addComment({ x: pending.x, y: pending.y, text: text.trim(), color: accentColor });\n setPending(null);\n setMode('view');\n onComment?.(comment);\n };\n\n const handleCancel = () => setPending(null);\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && e.metaKey) handleSubmit();\n if (e.key === 'Escape') handleCancel();\n };\n\n const POPOVER_WIDTH = 280;\n const left = Math.min(\n Math.max(pending.pixelX - POPOVER_WIDTH / 2, 8),\n pending.containerW - POPOVER_WIDTH - 8,\n );\n const showAbove = pending.pixelY > pending.containerH * 0.65;\n const top = showAbove ? pending.pixelY - 172 : pending.pixelY + 16;\n\n return (\n <>\n {/* Transparent backdrop — blocks accidental iframe clicks, dismisses on outside click */}\n <div style={{ position: 'fixed', inset: 0, zIndex: 9999 }} onClick={handleCancel} />\n\n <div\n style={{\n position: 'fixed',\n left,\n top,\n width: POPOVER_WIDTH,\n zIndex: 10000,\n backgroundColor: '#fff',\n borderRadius: 12,\n boxShadow: '0 8px 32px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.10)',\n border: '1px solid rgba(0,0,0,0.06)',\n padding: '14px 16px',\n fontFamily: \"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif\",\n }}\n >\n <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>\n <span style={{ fontSize: '0.875rem', fontWeight: 600, color: '#1f2937' }}>\n Add comment\n </span>\n <button\n onClick={handleCancel}\n style={{\n border: 'none', background: 'none', cursor: 'pointer',\n color: '#9ca3af', fontSize: 14, padding: '2px 4px', borderRadius: 4,\n }}\n >\n ✕\n </button>\n </div>\n\n <textarea\n style={{\n width: '100%', height: 80, padding: '8px 10px',\n border: '1px solid #e5e7eb', borderRadius: 8, fontSize: '0.8125rem',\n resize: 'none', boxSizing: 'border-box', lineHeight: 1.5,\n fontFamily: 'inherit', color: '#374151', outline: 'none',\n }}\n placeholder=\"Leave a comment...\"\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n autoFocus\n />\n\n <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 10 }}>\n <button\n style={{\n padding: '6px 12px', fontSize: '0.8125rem', border: '1px solid #e5e7eb',\n borderRadius: 6, backgroundColor: '#fff', cursor: 'pointer', color: '#6b7280',\n }}\n onClick={handleCancel}\n >\n Cancel\n </button>\n <button\n style={{\n padding: '6px 12px', fontSize: '0.8125rem', border: 'none',\n borderRadius: 6, backgroundColor: accentColor, color: '#fff',\n cursor: text.trim() ? 'pointer' : 'not-allowed',\n opacity: text.trim() ? 1 : 0.5,\n }}\n onClick={handleSubmit}\n disabled={!text.trim()}\n >\n Add\n </button>\n </div>\n </div>\n </>\n );\n}\n","import { useState } from 'react';\nimport { useWashiContext } from './WashiProvider';\nimport { WashiToolBubble } from './WashiToolBubble';\nimport type { WashiToolBubblePosition } from './WashiToolBubble';\nimport { WashiCommentsSidebar } from './WashiCommentsSidebar';\nimport { WashiPinDialog } from './WashiPinDialog';\n\n// Inject spinner keyframe once at module load\nconst SPINNER_ID = '__washi-ui-spinner__';\nif (typeof document !== 'undefined' && !document.getElementById(SPINNER_ID)) {\n const style = document.createElement('style');\n style.id = SPINNER_ID;\n style.textContent = `@keyframes __washi-spin { to { transform: rotate(360deg); } }`;\n document.head.appendChild(style);\n}\n\nexport interface WashiUIProps {\n /** Corner where the tool bubble (and sidebar) are anchored. Default: 'bottom-right' */\n position?: WashiToolBubblePosition;\n accentColor?: string;\n /** Show a loading spinner while the iframe initialises. Default: true */\n showLoader?: boolean;\n}\n\n/**\n * All-in-one Washi UI layer.\n *\n * Renders the tool bubble, comments sidebar, pin dialog, and an optional\n * loading overlay — all wired together. Drop it inside a `<WashiProvider>`.\n *\n * @example\n * ```tsx\n * <WashiProvider adapter={adapter}>\n * <div style={{ position: 'fixed', inset: 0 }}>\n * <WashiFrame src=\"/content.html\" style={{ width: '100%', height: '100%', border: 'none' }} />\n * </div>\n * <WashiUI />\n * </WashiProvider>\n * ```\n */\nexport function WashiUI({\n position = 'bottom-right',\n accentColor = '#667eea',\n showLoader = true,\n}: WashiUIProps) {\n const { isReady } = useWashiContext();\n const [sidebarOpen, setSidebarOpen] = useState(false);\n\n return (\n <>\n {/* Loading overlay */}\n {showLoader && !isReady && (\n <div\n style={{\n position: 'fixed', inset: 0, zIndex: 100,\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n backgroundColor: 'rgba(255,255,255,0.9)',\n }}\n >\n <div\n style={{\n width: 40, height: 40, borderRadius: '50%',\n border: '3px solid #e5e7eb',\n borderTopColor: accentColor,\n animation: '__washi-spin 1s linear infinite',\n }}\n />\n </div>\n )}\n\n <WashiToolBubble\n position={position}\n accentColor={accentColor}\n sidebarOpen={sidebarOpen}\n onSidebarToggle={() => setSidebarOpen((o) => !o)}\n />\n\n <WashiCommentsSidebar\n open={sidebarOpen}\n onClose={() => setSidebarOpen(false)}\n position={position}\n accentColor={accentColor}\n />\n\n <WashiPinDialog accentColor={accentColor} />\n </>\n );\n}\n"],"mappings":"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","names":["useRef","useState","useEffect","useCallback","Washi","useEffect","useRef","jsx","useRef","useEffect","React","jsx","React","useState","jsx","useState","useState","useEffect","jsx","jsxs","useState","useEffect","useState","useEffect","Fragment","jsx","jsxs","useState","useEffect","useState","Fragment","jsx","jsxs","useState"]}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@washi-ui/react",
3
+ "version": "1.0.0",
4
+ "description": "React bindings for Washi HTML commenting engine",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/washi-ui/washi.git",
21
+ "directory": "packages/react"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/washi-ui/washi/issues"
25
+ },
26
+ "homepage": "https://github.com/washi-ui/washi#readme",
27
+ "peerDependencies": {
28
+ "react": ">=18.0.0",
29
+ "react-dom": ">=18.0.0"
30
+ },
31
+ "dependencies": {
32
+ "@washi-ui/core": "1.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@testing-library/jest-dom": "^6.9.1",
36
+ "@testing-library/react": "^14.3.1",
37
+ "@types/react": "^18.2.0",
38
+ "@types/react-dom": "^18.2.0",
39
+ "react": "^18.2.0",
40
+ "react-dom": "^18.2.0"
41
+ },
42
+ "keywords": [
43
+ "react",
44
+ "html",
45
+ "comments",
46
+ "commenting",
47
+ "annotations",
48
+ "iframe",
49
+ "washi",
50
+ "hooks"
51
+ ],
52
+ "author": "Marco Chavez",
53
+ "license": "MIT",
54
+ "sideEffects": false,
55
+ "scripts": {
56
+ "dev": "tsup --watch",
57
+ "build": "tsup",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "lint": "tsc --noEmit"
61
+ }
62
+ }