@embedpdf/plugin-selection 2.5.0 → 2.6.1

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 (45) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +159 -81
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/handlers/marquee-selection.handler.d.ts +9 -7
  6. package/dist/lib/handlers/text-selection.handler.d.ts +8 -6
  7. package/dist/lib/selection-plugin.d.ts +7 -4
  8. package/dist/lib/types.d.ts +71 -2
  9. package/dist/preact/index.cjs +1 -1
  10. package/dist/preact/index.cjs.map +1 -1
  11. package/dist/preact/index.js +59 -16
  12. package/dist/preact/index.js.map +1 -1
  13. package/dist/react/index.cjs +1 -1
  14. package/dist/react/index.cjs.map +1 -1
  15. package/dist/react/index.js +59 -16
  16. package/dist/react/index.js.map +1 -1
  17. package/dist/shared/components/index.d.ts +1 -0
  18. package/dist/shared/components/marquee-selection.d.ts +18 -5
  19. package/dist/shared/components/selection-layer.d.ts +18 -1
  20. package/dist/shared/components/text-selection.d.ts +21 -0
  21. package/dist/shared-preact/components/index.d.ts +1 -0
  22. package/dist/shared-preact/components/marquee-selection.d.ts +18 -5
  23. package/dist/shared-preact/components/selection-layer.d.ts +18 -1
  24. package/dist/shared-preact/components/text-selection.d.ts +21 -0
  25. package/dist/shared-react/components/index.d.ts +1 -0
  26. package/dist/shared-react/components/marquee-selection.d.ts +18 -5
  27. package/dist/shared-react/components/selection-layer.d.ts +18 -1
  28. package/dist/shared-react/components/text-selection.d.ts +21 -0
  29. package/dist/svelte/components/MarqueeSelection.svelte.d.ts +12 -2
  30. package/dist/svelte/components/SelectionLayer.svelte.d.ts +11 -1
  31. package/dist/svelte/components/TextSelection.svelte.d.ts +22 -0
  32. package/dist/svelte/components/index.d.ts +1 -0
  33. package/dist/svelte/index.cjs +1 -1
  34. package/dist/svelte/index.cjs.map +1 -1
  35. package/dist/svelte/index.js +93 -17
  36. package/dist/svelte/index.js.map +1 -1
  37. package/dist/vue/components/index.d.ts +1 -0
  38. package/dist/vue/components/marquee-selection.vue.d.ts +13 -4
  39. package/dist/vue/components/selection-layer.vue.d.ts +15 -5
  40. package/dist/vue/components/text-selection.vue.d.ts +51 -0
  41. package/dist/vue/index.cjs +1 -1
  42. package/dist/vue/index.cjs.map +1 -1
  43. package/dist/vue/index.js +89 -32
  44. package/dist/vue/index.js.map +1 -1
  45. package/package.json +9 -9
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/index.ts","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/components/SelectionLayer.svelte"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px dashed ${stroke}`}\n style:background={fill}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for selection rectangles */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n const page = $derived(documentState.current?.document?.pages?.[pageIndex]);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived.by(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState.current?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n });\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\n rect={{\n origin: {\n x: placement.rect.origin.x * actualScale,\n y: placement.rect.origin.y * actualScale,\n },\n size: {\n width: placement.rect.size.width * actualScale,\n height: placement.rect.size.height * actualScale,\n },\n }}\n rotation={actualRotation}\n >\n {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","selectionCapability","$","user_effect","provides","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build","stroke","fill","selectionPlugin","documentState","useDocumentState","$$props","documentId","rect","state","actualScale","derived","scale","_a","current","set","plugin","registerMarqueeOnPage","pageIndex","onRectChange","newRect","div","root_1","clsx","class","styles","left","get","origin","x","top","y","width","size","height","consequent","background","page","_c","_b","document","pages","rects","proxy","boundingRect","placement","actualRotation","rotation","shouldRenderMenu","Boolean","isVisible","selectionMenu","selectionMenuSnippet","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","onMenuPlacement","newPlacement","first_child","fragment_1","index","$$anchor","div_1","root_2","styles_1","children","$$arg0","menuProps","menuWrapperProps","context","type","selected","suggestTop","spaceAbove","spaceBelow","buildMenuProps","result","result_component","spread_props","props","CounterRotate","$$render","consequent_3","consequent_4"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/TextSelection.svelte","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/index.ts","../../src/svelte/components/SelectionLayer.svelte"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface TextSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for text selection highlights. Default: 'rgba(33,150,243)' */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: TextSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n const page = $derived(documentState.current?.document?.pages?.[pageIndex]);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived.by(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState.current?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n });\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\n rect={{\n origin: {\n x: placement.rect.origin.x * actualScale,\n y: placement.rect.origin.y * actualScale,\n },\n size: {\n width: placement.rect.size.width * actualScale,\n height: placement.rect.size.height * actualScale,\n },\n }}\n rotation={actualRotation}\n >\n {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Fill/background color inside the marquee rectangle. Default: 'rgba(0,122,204,0.15)' */\n background?: string;\n /** Border color of the marquee rectangle. Default: 'rgba(0,122,204,0.8)' */\n borderColor?: string;\n /** Border style. Default: 'dashed' */\n borderStyle?: 'solid' | 'dashed' | 'dotted';\n /**\n * @deprecated Use `borderColor` instead.\n */\n stroke?: string;\n /**\n * @deprecated Use `background` instead.\n */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n background,\n borderColor,\n borderStyle = 'dashed',\n stroke,\n fill,\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n // Resolve deprecated props: new CSS-standard props take precedence\n const resolvedBorderColor = $derived(borderColor ?? stroke ?? 'rgba(0,122,204,0.8)');\n const resolvedBackground = $derived(background ?? fill ?? 'rgba(0,122,204,0.15)');\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px ${borderStyle} ${resolvedBorderColor}`}\n style:background={resolvedBackground}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\n","<script lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import { Rotation } from '@embedpdf/models';\n import type { TextSelectionStyle, MarqueeSelectionStyle } from '@embedpdf/plugin-selection';\n import type { SelectionSelectionMenuRenderFn, SelectionSelectionMenuProps } from '../types';\n import TextSelection from './TextSelection.svelte';\n import MarqueeSelection from './MarqueeSelection.svelte';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /**\n * @deprecated Use `textStyle.background` instead.\n * Background color for selection rectangles.\n */\n background?: string;\n /** Styling options for text selection highlights */\n textStyle?: TextSelectionStyle;\n /** Styling options for the marquee selection rectangle */\n marqueeStyle?: MarqueeSelectionStyle;\n /** Optional CSS class applied to the marquee rectangle */\n marqueeClass?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale,\n rotation,\n background,\n textStyle,\n marqueeStyle,\n marqueeClass,\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const resolvedTextBackground = $derived(textStyle?.background ?? background);\n</script>\n\n<TextSelection\n {documentId}\n {pageIndex}\n {scale}\n {rotation}\n background={resolvedTextBackground}\n {selectionMenu}\n {selectionMenuSnippet}\n/>\n<MarqueeSelection\n {documentId}\n {pageIndex}\n {scale}\n background={marqueeStyle?.background}\n borderColor={marqueeStyle?.borderColor}\n borderStyle={marqueeStyle?.borderStyle}\n class={marqueeClass}\n/>\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","background","selectionPlugin","documentState","useDocumentState","$$props","documentId","page","_c","_b","_a","current","document","pages","pageIndex","rects","$","state","proxy","boundingRect","placement","actualScale","derived","scale","actualRotation","rotation","get","shouldRenderMenu","Boolean","isVisible","selectionMenu","selectionMenuSnippet","user_effect","plugin","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","set","onMenuPlacement","newPlacement","div","first_child","fragment_1","index","$$anchor","rect","div_1","root_2","styles_1","origin","x","y","width","size","height","children","$$arg0","menuProps","menuWrapperProps","context","type","selected","suggestTop","spaceAbove","spaceBelow","buildMenuProps","result","result_component","spread_props","props","consequent","CounterRotate","$$render","consequent_3","styles","left","top","consequent_4","borderStyle","resolvedBorderColor","resolvedBackground","registerMarqueeOnPage","onRectChange","newRect","root_1","clsx","class","border","selectionCapability","provides","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build","resolvedTextBackground","TextSelection","node","borderColor","MarqueeSelection","node_1"],"mappings":"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"}
@@ -10,7 +10,7 @@ const useSelectionCapability = () => useCapability(SelectionPlugin.id);
10
10
  const useSelectionPlugin = () => usePlugin(SelectionPlugin.id);
11
11
  var root_2 = $.from_html(`<div></div>`);
12
12
  var root_1$1 = $.from_html(`<div></div> <!>`, 1);
13
- function SelectionLayer($$anchor, $$props) {
13
+ function TextSelection($$anchor, $$props) {
14
14
  $.push($$props, true);
15
15
  let background = $.prop($$props, "background", 3, "rgba(33,150,243)");
16
16
  const selectionPlugin = useSelectionPlugin();
@@ -203,25 +203,14 @@ function SelectionLayer($$anchor, $$props) {
203
203
  $.append($$anchor, fragment);
204
204
  $.pop();
205
205
  }
206
- function CopyToClipboard($$anchor, $$props) {
207
- $.push($$props, true);
208
- const selectionCapability = useSelectionCapability();
209
- $.user_effect(() => {
210
- if (!selectionCapability.provides) return;
211
- return selectionCapability.provides.onCopyToClipboard(({ text }) => {
212
- navigator.clipboard.writeText(text).catch((err) => {
213
- console.error("Failed to copy text to clipboard:", err);
214
- });
215
- });
216
- });
217
- $.pop();
218
- }
219
206
  var root_1 = $.from_html(`<div></div>`);
220
207
  function MarqueeSelection($$anchor, $$props) {
221
208
  $.push($$props, true);
222
- let stroke = $.prop($$props, "stroke", 3, "rgba(0,122,204,0.8)"), fill = $.prop($$props, "fill", 3, "rgba(0,122,204,0.15)");
209
+ let borderStyle = $.prop($$props, "borderStyle", 3, "dashed");
223
210
  const selectionPlugin = useSelectionPlugin();
224
211
  const documentState = useDocumentState(() => $$props.documentId);
212
+ const resolvedBorderColor = $.derived(() => $$props.borderColor ?? $$props.stroke ?? "rgba(0,122,204,0.8)");
213
+ const resolvedBackground = $.derived(() => $$props.background ?? $$props.fill ?? "rgba(0,122,204,0.15)");
225
214
  let rect = $.state(null);
226
215
  const actualScale = $.derived(() => {
227
216
  var _a;
@@ -256,8 +245,8 @@ function MarqueeSelection($$anchor, $$props) {
256
245
  top: `${$.get(rect).origin.y * $.get(actualScale)}px`,
257
246
  width: `${$.get(rect).size.width * $.get(actualScale)}px`,
258
247
  height: `${$.get(rect).size.height * $.get(actualScale)}px`,
259
- border: `1px dashed ${stroke()}`,
260
- background: fill(),
248
+ border: `1px ${borderStyle()} ${$.get(resolvedBorderColor)}`,
249
+ background: $.get(resolvedBackground),
261
250
  "box-sizing": "border-box",
262
251
  "z-index": "1000"
263
252
  });
@@ -271,12 +260,99 @@ function MarqueeSelection($$anchor, $$props) {
271
260
  $.append($$anchor, fragment);
272
261
  $.pop();
273
262
  }
263
+ var root = $.from_html(`<!> <!>`, 1);
264
+ function SelectionLayer($$anchor, $$props) {
265
+ $.push($$props, true);
266
+ const resolvedTextBackground = $.derived(() => {
267
+ var _a;
268
+ return ((_a = $$props.textStyle) == null ? void 0 : _a.background) ?? $$props.background;
269
+ });
270
+ var fragment = root();
271
+ var node = $.first_child(fragment);
272
+ TextSelection(node, {
273
+ get documentId() {
274
+ return $$props.documentId;
275
+ },
276
+ get pageIndex() {
277
+ return $$props.pageIndex;
278
+ },
279
+ get scale() {
280
+ return $$props.scale;
281
+ },
282
+ get rotation() {
283
+ return $$props.rotation;
284
+ },
285
+ get background() {
286
+ return $.get(resolvedTextBackground);
287
+ },
288
+ get selectionMenu() {
289
+ return $$props.selectionMenu;
290
+ },
291
+ get selectionMenuSnippet() {
292
+ return $$props.selectionMenuSnippet;
293
+ }
294
+ });
295
+ var node_1 = $.sibling(node, 2);
296
+ {
297
+ let $0 = $.derived(() => {
298
+ var _a;
299
+ return (_a = $$props.marqueeStyle) == null ? void 0 : _a.background;
300
+ });
301
+ let $1 = $.derived(() => {
302
+ var _a;
303
+ return (_a = $$props.marqueeStyle) == null ? void 0 : _a.borderColor;
304
+ });
305
+ let $2 = $.derived(() => {
306
+ var _a;
307
+ return (_a = $$props.marqueeStyle) == null ? void 0 : _a.borderStyle;
308
+ });
309
+ MarqueeSelection(node_1, {
310
+ get documentId() {
311
+ return $$props.documentId;
312
+ },
313
+ get pageIndex() {
314
+ return $$props.pageIndex;
315
+ },
316
+ get scale() {
317
+ return $$props.scale;
318
+ },
319
+ get background() {
320
+ return $.get($0);
321
+ },
322
+ get borderColor() {
323
+ return $.get($1);
324
+ },
325
+ get borderStyle() {
326
+ return $.get($2);
327
+ },
328
+ get class() {
329
+ return $$props.marqueeClass;
330
+ }
331
+ });
332
+ }
333
+ $.append($$anchor, fragment);
334
+ $.pop();
335
+ }
336
+ function CopyToClipboard($$anchor, $$props) {
337
+ $.push($$props, true);
338
+ const selectionCapability = useSelectionCapability();
339
+ $.user_effect(() => {
340
+ if (!selectionCapability.provides) return;
341
+ return selectionCapability.provides.onCopyToClipboard(({ text }) => {
342
+ navigator.clipboard.writeText(text).catch((err) => {
343
+ console.error("Failed to copy text to clipboard:", err);
344
+ });
345
+ });
346
+ });
347
+ $.pop();
348
+ }
274
349
  const SelectionPluginPackage = createPluginPackage(SelectionPluginPackage$1).addUtility(CopyToClipboard).build();
275
350
  export {
276
351
  CopyToClipboard,
277
352
  MarqueeSelection,
278
353
  SelectionLayer,
279
354
  SelectionPluginPackage,
355
+ TextSelection,
280
356
  useSelectionCapability,
281
357
  useSelectionPlugin
282
358
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/SelectionLayer.svelte","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for selection rectangles */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n const page = $derived(documentState.current?.document?.pages?.[pageIndex]);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived.by(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState.current?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n });\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\n rect={{\n origin: {\n x: placement.rect.origin.x * actualScale,\n y: placement.rect.origin.y * actualScale,\n },\n size: {\n width: placement.rect.size.width * actualScale,\n height: placement.rect.size.height * actualScale,\n },\n }}\n rotation={actualRotation}\n >\n {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n","<script lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\n","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n stroke = 'rgba(0,122,204,0.8)',\n fill = 'rgba(0,122,204,0.15)',\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px dashed ${stroke}`}\n style:background={fill}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n"],"names":["$$anchor","BaseSelectionPluginPackage"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/svelte/hooks/use-selection.svelte.ts","../../src/svelte/components/TextSelection.svelte","../../src/svelte/components/MarqueeSelection.svelte","../../src/svelte/components/SelectionLayer.svelte","../../src/svelte/components/CopyToClipboard.svelte","../../src/svelte/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/svelte';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import type { Rect } from '@embedpdf/models';\n import { Rotation } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import {\n CounterRotate,\n type MenuWrapperProps,\n type SelectionMenuPlacement,\n } from '@embedpdf/utils/svelte';\n import { useSelectionPlugin } from '../hooks/use-selection.svelte';\n import type {\n SelectionSelectionMenuRenderFn,\n SelectionSelectionMenuProps,\n SelectionSelectionContext,\n } from '../types';\n import type { SelectionMenuPlacement as UtilsSelectionMenuPlacement } from '@embedpdf/plugin-selection';\n\n interface TextSelectionProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /** Background color for text selection highlights. Default: 'rgba(33,150,243)' */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n rotation: rotationOverride,\n background = 'rgba(33,150,243)',\n selectionMenu,\n selectionMenuSnippet,\n }: TextSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n const page = $derived(documentState.current?.document?.pages?.[pageIndex]);\n\n let rects = $state<Rect[]>([]);\n let boundingRect = $state<Rect | null>(null);\n let placement = $state<UtilsSelectionMenuPlacement | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n const actualRotation = $derived.by(() => {\n if (rotationOverride !== undefined) return rotationOverride;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page?.rotation ?? 0;\n const docRotation = documentState.current?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n });\n\n // Check if menu should render: placement is valid AND (render fn OR snippet exists)\n const shouldRenderMenu = $derived(\n Boolean(\n placement &&\n placement.pageIndex === pageIndex &&\n placement.isVisible &&\n (selectionMenu || selectionMenuSnippet),\n ),\n );\n\n // Track selection rectangles on this page\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n rects = [];\n boundingRect = null;\n return;\n }\n\n return selectionPlugin.plugin.registerSelectionOnPage({\n documentId,\n pageIndex,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects = newRects;\n boundingRect = newBoundingRect;\n },\n });\n });\n\n // Track menu placement for this document\n $effect(() => {\n if (!selectionPlugin.plugin || !documentId) {\n placement = null;\n return;\n }\n\n return selectionPlugin.plugin.onMenuPlacement(documentId, (newPlacement) => {\n placement = newPlacement;\n });\n });\n\n // --- Selection Menu Logic ---\n\n // Build context object for selection menu\n function buildContext(): SelectionSelectionContext {\n return {\n type: 'selection',\n pageIndex,\n };\n }\n\n // Build placement hints from plugin placement data\n function buildMenuPlacement(): SelectionMenuPlacement {\n return {\n suggestTop: placement?.suggestTop ?? false,\n spaceAbove: placement?.spaceAbove ?? 0,\n spaceBelow: placement?.spaceBelow ?? 0,\n };\n }\n\n // Build menu props\n function buildMenuProps(\n rect: Rect,\n menuWrapperProps: MenuWrapperProps,\n ): SelectionSelectionMenuProps {\n return {\n context: buildContext(),\n selected: true, // Selection is always \"selected\" when visible\n rect,\n placement: buildMenuPlacement(),\n menuWrapperProps,\n };\n }\n</script>\n\n{#if boundingRect}\n <!-- Highlight layer -->\n <div\n style:position=\"absolute\"\n style:left={`${boundingRect.origin.x * actualScale}px`}\n style:top={`${boundingRect.origin.y * actualScale}px`}\n style:width={`${boundingRect.size.width * actualScale}px`}\n style:height={`${boundingRect.size.height * actualScale}px`}\n style:mix-blend-mode=\"multiply\"\n style:isolation=\"isolate\"\n style:pointer-events=\"none\"\n >\n {#each rects as rect, i (i)}\n <div\n style:position=\"absolute\"\n style:left={`${(rect.origin.x - boundingRect.origin.x) * actualScale}px`}\n style:top={`${(rect.origin.y - boundingRect.origin.y) * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:background\n style:pointer-events=\"none\"\n ></div>\n {/each}\n </div>\n\n <!-- Selection menu (counter-rotated) -->\n {#if shouldRenderMenu && placement}\n <CounterRotate\n rect={{\n origin: {\n x: placement.rect.origin.x * actualScale,\n y: placement.rect.origin.y * actualScale,\n },\n size: {\n width: placement.rect.size.width * actualScale,\n height: placement.rect.size.height * actualScale,\n },\n }}\n rotation={actualRotation}\n >\n {#snippet children({ rect, menuWrapperProps })}\n {@const menuProps = buildMenuProps(rect, menuWrapperProps)}\n {#if selectionMenu}\n <!-- Priority 1: Render function (schema-driven) -->\n {@const result = selectionMenu(menuProps)}\n {#if result}\n <result.component {...result.props} />\n {/if}\n {:else if selectionMenuSnippet}\n <!-- Priority 2: Snippet (manual customization) -->\n {@render selectionMenuSnippet(menuProps)}\n {/if}\n {/snippet}\n </CounterRotate>\n {/if}\n{/if}\n","<script lang=\"ts\">\n import type { Rect } from '@embedpdf/models';\n import { useDocumentState } from '@embedpdf/core/svelte';\n import { useSelectionPlugin } from '../hooks';\n\n interface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n class?: string;\n /** Fill/background color inside the marquee rectangle. Default: 'rgba(0,122,204,0.15)' */\n background?: string;\n /** Border color of the marquee rectangle. Default: 'rgba(0,122,204,0.8)' */\n borderColor?: string;\n /** Border style. Default: 'dashed' */\n borderStyle?: 'solid' | 'dashed' | 'dotted';\n /**\n * @deprecated Use `borderColor` instead.\n */\n stroke?: string;\n /**\n * @deprecated Use `background` instead.\n */\n fill?: string;\n }\n\n let {\n documentId,\n pageIndex,\n scale: scaleOverride,\n class: propsClass,\n background,\n borderColor,\n borderStyle = 'dashed',\n stroke,\n fill,\n }: MarqueeSelectionProps = $props();\n\n const selectionPlugin = useSelectionPlugin();\n const documentState = useDocumentState(() => documentId);\n\n // Resolve deprecated props: new CSS-standard props take precedence\n const resolvedBorderColor = $derived(borderColor ?? stroke ?? 'rgba(0,122,204,0.8)');\n const resolvedBackground = $derived(background ?? fill ?? 'rgba(0,122,204,0.15)');\n\n let rect = $state<Rect | null>(null);\n\n const actualScale = $derived(\n scaleOverride !== undefined ? scaleOverride : (documentState.current?.scale ?? 1),\n );\n\n $effect(() => {\n rect = null;\n\n if (!selectionPlugin.plugin) {\n return;\n }\n\n return selectionPlugin.plugin.registerMarqueeOnPage({\n documentId,\n pageIndex,\n scale: actualScale,\n onRectChange: (newRect) => {\n rect = newRect;\n },\n });\n });\n</script>\n\n{#if rect}\n <div\n style:position=\"absolute\"\n style:pointer-events=\"none\"\n style:left={`${rect.origin.x * actualScale}px`}\n style:top={`${rect.origin.y * actualScale}px`}\n style:width={`${rect.size.width * actualScale}px`}\n style:height={`${rect.size.height * actualScale}px`}\n style:border={`1px ${borderStyle} ${resolvedBorderColor}`}\n style:background={resolvedBackground}\n style:box-sizing=\"border-box\"\n style:z-index=\"1000\"\n class={propsClass}\n ></div>\n{/if}\n","<script lang=\"ts\">\n import type { Snippet } from 'svelte';\n import { Rotation } from '@embedpdf/models';\n import type { TextSelectionStyle, MarqueeSelectionStyle } from '@embedpdf/plugin-selection';\n import type { SelectionSelectionMenuRenderFn, SelectionSelectionMenuProps } from '../types';\n import TextSelection from './TextSelection.svelte';\n import MarqueeSelection from './MarqueeSelection.svelte';\n\n interface SelectionLayerProps {\n /** Document ID */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Rotation of the page (optional, defaults to document rotation) */\n rotation?: Rotation;\n /**\n * @deprecated Use `textStyle.background` instead.\n * Background color for selection rectangles.\n */\n background?: string;\n /** Styling options for text selection highlights */\n textStyle?: TextSelectionStyle;\n /** Styling options for the marquee selection rectangle */\n marqueeStyle?: MarqueeSelectionStyle;\n /** Optional CSS class applied to the marquee rectangle */\n marqueeClass?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n /** Snippet for custom selection menu (slot-based approach) */\n selectionMenuSnippet?: Snippet<[SelectionSelectionMenuProps]>;\n }\n\n let {\n documentId,\n pageIndex,\n scale,\n rotation,\n background,\n textStyle,\n marqueeStyle,\n marqueeClass,\n selectionMenu,\n selectionMenuSnippet,\n }: SelectionLayerProps = $props();\n\n const resolvedTextBackground = $derived(textStyle?.background ?? background);\n</script>\n\n<TextSelection\n {documentId}\n {pageIndex}\n {scale}\n {rotation}\n background={resolvedTextBackground}\n {selectionMenu}\n {selectionMenuSnippet}\n/>\n<MarqueeSelection\n {documentId}\n {pageIndex}\n {scale}\n background={marqueeStyle?.background}\n borderColor={marqueeStyle?.borderColor}\n borderStyle={marqueeStyle?.borderStyle}\n class={marqueeClass}\n/>\n","<script lang=\"ts\">\n import { useSelectionCapability } from '../hooks/use-selection.svelte';\n\n const selectionCapability = useSelectionCapability();\n\n $effect(() => {\n if (!selectionCapability.provides) return;\n\n return selectionCapability.provides.onCopyToClipboard(({ text }) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n });\n</script>\n\n<!-- This component renders nothing to the DOM -->\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n"],"names":["$$anchor","BaseSelectionPluginPackage"],"mappings":";;;;;;;;AAOa,MAAA,yBAAA,MAA+B,cAA+B,gBAAgB,EAAE;AAMhF,MAAA,qBAAA,MAA2B,UAA2B,gBAAgB,EAAE;;;0CCbrF;;AAwCI,MAAA,8CAAa,kBAAkB;AAK3B,QAAA,kBAAkB,mBAAkB;AACpC,QAAA,gBAAgB,iBAAgB,MAAA,QAAA,UAAA;AAEhC,QAAA;;AAAgB,2CAAc,YAAd,mBAAuB,aAAvB,mBAAiC,UAAjC,mBAAsC,QAAA;AAAA,GAAA;AAExD,MAAA,QAAQ,EAAA,MAAM,EAAA,MAAA,CAAA,CAAA,CAAA;MACd,eAAe,EAAA,MAAoB,IAAI;MACvC,YAAY,EAAA,MAA2C,IAAI;QAEzD,cAAW,EAAA,QAAA,MAAA;;AAAA,mBAAA,UACG,SAAS,QAAA,UAAoB,mBAAc,YAAd,mBAAuB,UAAS;AAAA,GAAC;AAG5E,QAAA,iBAAc,EAAA,QAAA,MAAqB;;6BACd,OAAS,QAAA,QAAA;AAE5B,UAAA,iBAAY,OAAA,IAAG,IAAI,MAAP,mBAAS,aAAY;AACjC,UAAA,gBAAc,mBAAc,YAAd,mBAAuB,aAAY;YAC9C,eAAe,eAAe;AAAA,EACzC,CAAC;QAGK,mBAAgB,EAAA,QAAA,MACpB,QAAO,EAAA,IACL,SAAS,KAAA,EAAA,IACT,SAAS,EAAC,cAAS,QAAA,aAAA,EAAA,IACnB,SAAS,EAAC,cAAS,QAAA,iBAAA,QAAA,qBAAA,CAAA;AAMvB,IAAA,YAAO,MAAO;SACP,gBAAgB,UAAM,CAAA,QAAA,YAAiB;YAC1C,OAAK,CAAA,GAAA,IAAA;AACL,QAAA,IAAA,cAAe,IAAI;;IAErB;WAEO,gBAAgB,OAAO,wBAAuB;AAAA,MACnD,YAAU,QAAA;AAAA,MACV,WAAS,QAAA;AAAA,MACT,eAAa,CAAA,EAAK,OAAO,UAAU,cAAc,gBAAe,MAAO;AACrE,UAAA,IAAA,OAAQ,UAAQ,IAAA;AAChB,UAAA,IAAA,cAAe,iBAAe,IAAA;AAAA,MAChC;AAAA;EAEJ,CAAC;AAGD,IAAA,YAAO,MAAO;SACP,gBAAgB,UAAM,CAAA,QAAA,YAAiB;AAC1C,QAAA,IAAA,WAAY,IAAI;;IAElB;AAEO,WAAA,gBAAgB,OAAO,gBAAe,QAAA,YAAA,CAAc,iBAAiB;AAC1E,QAAA,IAAA,WAAY,cAAY,IAAA;AAAA,IAC1B,CAAC;AAAA,EACH,CAAC;AAKQ,WAAA,eAA0C;aAE/C,MAAM,aACN,WAAS,QAAA,UAAA;AAAA,EAEb;AAGS,WAAA,qBAA6C;;;MAElD,cAAU,OAAA,IAAE,SAAS,MAAX,mBAAa,eAAc;AAAA,MACrC,cAAU,OAAA,IAAE,SAAS,MAAX,mBAAa,eAAc;AAAA,MACrC,cAAU,OAAA,IAAE,SAAS,MAAX,mBAAa,eAAc;AAAA;EAEzC;AAGS,WAAA,eACP,MACA,kBAC6B;;MAE3B,SAAS,aAAY;AAAA,MACrB,UAAU;AAAA;AAAA,MACV;AAAA,MACA,WAAW,mBAAkB;AAAA,MAC7B;AAAA;EAEJ;;;;;;UAKC,MAAE,EAAA,YAAA,UAAA;;aAAF,KAAE,IAAA,MAAA,EAAA,IAUM,KAAK,GAAA,EAAA,OAAA,CAAAA,WAAI,SAAI;YACjB,QAAE,OAAA;;uDAAF,OAAE,IAAA,UAAA;AAAA;0BAEe,IAAI,EAAC,OAAO,IAAC,EAAA,IAAG,YAAY,EAAC,OAAO,KAAC,EAAA,IAAI,WAAW,CAAA;AAAA,yBACrD,IAAI,EAAC,OAAO,IAAC,EAAA,IAAG,YAAY,EAAC,OAAO,KAAC,EAAA,IAAI,WAAW,CAAA;AAAA,UACnD,OAAA,GAAA,EAAA,IAAA,IAAI,EAAC,KAAK,cAAQ,WAAW,CAAA;AAAA,UAC5B,QAAA,GAAA,EAAA,IAAA,IAAI,EAAC,KAAK,eAAS,WAAW,CAAA;AAAA;;;4BALhD,KAAE;AAAA;cAXN,GAAE;6BAAF,KAAE,CAAA;;;;kBAsCW,WAAQ,CAAAA,WAAA,WAAA;AAAG,kBAAA,gDAAA;AAAM,kBAAA,4DAAA;AACjB,oBAAA,YAAS,EAAA,QAAA,MAAG,eAAe,KAAI,GAAE,iBAAgB,CAAA,CAAA;;;;;AAG/C,wBAAA,qDAAuB,SAAS,CAAA,CAAA;;;;;;;;AAErC,yCAAgBA,WAAA,EAAA,aAAA,MAAA,EAAA,IAAK,MAAM,EAAC,KAAK,CAAA;AAAA;;;;gCAD/B,MAAM,EAAA,UAAA,UAAA;AAAA;;;;;;;;;;;wFAKmB,SAAS,CAAA;;;;;;;;;;;;;;;;;;;;;cArBzC,QAAM;AAAA,gBACJ,GAAC,EAAA,IAAE,SAAS,EAAC,KAAK,OAAO,IAAC,EAAA,IAAG,WAAW;AAAA,gBACxC,GAAC,EAAA,IAAE,SAAS,EAAC,KAAK,OAAO,IAAC,EAAA,IAAG,WAAW;AAAA;cAE1C,MAAI;AAAA,gBACF,OAAK,EAAA,IAAE,SAAS,EAAC,KAAK,KAAK,QAAK,EAAA,IAAG,WAAW;AAAA,gBAC9C,QAAM,EAAA,IAAE,SAAS,EAAC,KAAK,KAAK,SAAM,EAAA,IAAG,WAAW;AAAA;;AARrD,0BAAYA,WAAA;AAAA;;;;6BAWD,cAAc;AAAA;cAEd;AAAA;;;;;AAdT,cAAA,EAAA,IAAA,gBAAgB,WAAI,SAAS,EAAA,UAAA,YAAA;AAAA;;mDAxBjC,KAAE,IAAA,QAAA;AAAA;QAEc,MAAA,GAAA,EAAA,IAAA,YAAY,EAAC,OAAO,UAAI,WAAW,CAAA;AAAA,QACpC,KAAA,GAAA,EAAA,IAAA,YAAY,EAAC,OAAO,UAAI,WAAW,CAAA;AAAA,QACjC,OAAA,GAAA,EAAA,IAAA,YAAY,EAAC,KAAK,cAAQ,WAAW,CAAA;AAAA,QACpC,QAAA,GAAA,EAAA,IAAA,YAAY,EAAC,KAAK,eAAS,WAAW,CAAA;AAAA;;;;;;;gBAPtD,YAAY,EAAA,UAAA,YAAA;AAAA;;;;AAFT;;6CC1IR;;AAqCI,MAAA,gDAAc,QAAQ;AAKlB,QAAA,kBAAkB,mBAAkB;AACpC,QAAA,gBAAgB,iBAAgB,MAAA,QAAA,UAAA;AAGhC,QAAA,+EAAwD,qBAAqB;AAC7E,QAAA,2EAAoD,sBAAsB;MAE5E,OAAO,EAAA,MAAoB,IAAI;QAE7B,cAAW,EAAA,QAAA,MAAA;;AAAA,mBAAA,UACG,SAAS,QAAA,UAAoB,mBAAc,YAAd,mBAAuB,UAAS;AAAA,GAAC;AAGlF,IAAA,YAAO,MAAO;AACZ,MAAA,IAAA,MAAO,IAAI;SAEN,gBAAgB,QAAQ;;IAE7B;WAEO,gBAAgB,OAAO,sBAAqB;AAAA,MACjD,YAAU,QAAA;AAAA,MACV,WAAS,QAAA;AAAA,MACT,aAAO,WAAW;AAAA,MAClB,cAAY,CAAG,YAAY;AACzB,UAAA,IAAA,MAAO,SAAO,IAAA;AAAA,MAChB;AAAA;EAEJ,CAAC;;;;;UAIA,MAAE,OAAA;;;oBAAF,KAAE,GAAA,EAAA,KAAA,QAAA,KAAA,CAAA;6BAAF,KAAE,IAAA,QAAA;AAAA;;UAGc,MAAA,GAAA,EAAA,IAAA,IAAI,EAAC,OAAO,UAAI,WAAW,CAAA;AAAA,UAC5B,KAAA,GAAA,EAAA,IAAA,IAAI,EAAC,OAAO,UAAI,WAAW,CAAA;AAAA,UACzB,OAAA,GAAA,EAAA,IAAA,IAAI,EAAC,KAAK,cAAQ,WAAW,CAAA;AAAA,UAC5B,QAAA,GAAA,EAAA,IAAA,IAAI,EAAC,KAAK,eAAS,WAAW,CAAA;AAAA,UAC1B,QAAA,OAAA,YAAW,WAAI,mBAAmB,CAAA;AAAA,4BACrC,kBAAkB;AAAA;;;;0BARrC,GAAE;AAAA;;gBADA,IAAI,EAAA,UAAA,UAAA;AAAA;;;;AAFD;;2CCvER;;AA+CQ,QAAA;;2DAA6C,eAAU,QAAA;AAAA,GAAA;;;AAG9D,gBAAY,MAAA;AAAA;;;;;;;;;;;;;mBAKC,sBAAsB;AAAA;;;;;;;;;;;;+DAQR;AAAA,KAAU;;;+DACT;AAAA,KAAW;;;+DACX;AAAA,KAAW;AANvC,qBAAe,QAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;AAXR;4CChDR;;AAGQ,QAAA,sBAAsB,uBAAsB;AAElD,IAAA,YAAO,MAAO;AACP,QAAA,CAAA,oBAAoB,SAAQ;AAE1B,WAAA,oBAAoB,SAAS,kBAAiB,CAAA,EAAI,KAAI,MAAO;AAClE,gBAAU,UAAU,UAAU,IAAI,EAAE,MAAK,CAAE,QAAQ;AACjD,gBAAQ,MAAM,qCAAqC,GAAG;AAAA,MACxD,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;;AACK;ACJD,MAAM,yBAAyB,oBAAoBC,wBAA0B,EACjF,WAAW,eAAe,EAC1B,MAAA;"}
@@ -1,3 +1,4 @@
1
1
  export { default as SelectionLayer } from './selection-layer.vue';
2
+ export { default as TextSelection } from './text-selection.vue';
2
3
  export { default as CopyToClipboard } from './copy-to-clipboard.vue';
3
4
  export { default as MarqueeSelection } from './marquee-selection.vue';
@@ -7,14 +7,23 @@ interface MarqueeSelectionProps {
7
7
  scale?: number;
8
8
  /** Optional CSS class applied to the marquee rectangle */
9
9
  className?: string;
10
- /** Stroke colour (default: 'rgba(0,122,204,0.8)') */
10
+ /** Fill/background color inside the marquee rectangle. Default: 'rgba(0,122,204,0.15)' */
11
+ background?: string;
12
+ /** Border color of the marquee rectangle. Default: 'rgba(0,122,204,0.8)' */
13
+ borderColor?: string;
14
+ /** Border style. Default: 'dashed' */
15
+ borderStyle?: 'solid' | 'dashed' | 'dotted';
16
+ /**
17
+ * @deprecated Use `borderColor` instead.
18
+ */
11
19
  stroke?: string;
12
- /** Fill colour (default: 'rgba(0,122,204,0.15)') */
20
+ /**
21
+ * @deprecated Use `background` instead.
22
+ */
13
23
  fill?: string;
14
24
  }
15
25
  declare const __VLS_export: import('vue').DefineComponent<MarqueeSelectionProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<MarqueeSelectionProps> & Readonly<{}>, {
16
- fill: string;
17
- stroke: string;
26
+ borderStyle: "solid" | "dashed" | "dotted";
18
27
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
19
28
  declare const _default: typeof __VLS_export;
20
29
  export default _default;
@@ -1,16 +1,27 @@
1
1
  import { Rotation } from '@embedpdf/models';
2
- import { SelectionSelectionContext, SelectionSelectionMenuRenderFn } from '../types';
2
+ import { TextSelectionStyle, MarqueeSelectionStyle } from '../../lib/index.ts';
3
+ import { SelectionSelectionMenuRenderFn } from '../types';
3
4
  interface SelectionLayerProps {
4
5
  documentId: string;
5
6
  pageIndex: number;
6
7
  scale?: number;
7
8
  rotation?: Rotation;
9
+ /**
10
+ * @deprecated Use `textStyle.background` instead.
11
+ * Background color for selection rectangles.
12
+ */
8
13
  background?: string;
14
+ /** Styling options for text selection highlights */
15
+ textStyle?: TextSelectionStyle;
16
+ /** Styling options for the marquee selection rectangle */
17
+ marqueeStyle?: MarqueeSelectionStyle;
18
+ /** Optional CSS class applied to the marquee rectangle */
19
+ marqueeClassName?: string;
9
20
  /** Render function for selection menu (schema-driven approach) */
10
21
  selectionMenu?: SelectionSelectionMenuRenderFn;
11
22
  }
12
- declare var __VLS_13: {
13
- context: SelectionSelectionContext;
23
+ declare var __VLS_8: {
24
+ context: import('..').SelectionSelectionContext;
14
25
  selected: boolean;
15
26
  rect: {
16
27
  origin: {
@@ -34,11 +45,10 @@ declare var __VLS_13: {
34
45
  };
35
46
  };
36
47
  type __VLS_Slots = {} & {
37
- 'selection-menu'?: (props: typeof __VLS_13) => any;
48
+ 'selection-menu'?: (props: typeof __VLS_8) => any;
38
49
  };
39
50
  declare const __VLS_base: import('vue').DefineComponent<SelectionLayerProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<SelectionLayerProps> & Readonly<{}>, {
40
51
  rotation: Rotation;
41
- background: string;
42
52
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
43
53
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
44
54
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,51 @@
1
+ import { Rotation } from '@embedpdf/models';
2
+ import { SelectionSelectionContext, SelectionSelectionMenuRenderFn } from '../types';
3
+ interface TextSelectionProps {
4
+ documentId: string;
5
+ pageIndex: number;
6
+ scale?: number;
7
+ rotation?: Rotation;
8
+ /** Background color for text selection highlights. Default: 'rgba(33,150,243)' */
9
+ background?: string;
10
+ /** Render function for selection menu (schema-driven approach) */
11
+ selectionMenu?: SelectionSelectionMenuRenderFn;
12
+ }
13
+ declare var __VLS_13: {
14
+ context: SelectionSelectionContext;
15
+ selected: boolean;
16
+ rect: {
17
+ origin: {
18
+ x: number;
19
+ y: number;
20
+ };
21
+ size: {
22
+ width: number;
23
+ height: number;
24
+ };
25
+ };
26
+ placement: {
27
+ suggestTop: boolean;
28
+ spaceAbove: number;
29
+ spaceBelow: number;
30
+ };
31
+ menuWrapperProps: {
32
+ style: import('vue').CSSProperties;
33
+ onPointerdown: (e: PointerEvent) => void;
34
+ onTouchstart: (e: TouchEvent) => void;
35
+ };
36
+ };
37
+ type __VLS_Slots = {} & {
38
+ 'selection-menu'?: (props: typeof __VLS_13) => any;
39
+ };
40
+ declare const __VLS_base: import('vue').DefineComponent<TextSelectionProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<TextSelectionProps> & Readonly<{}>, {
41
+ rotation: Rotation;
42
+ background: string;
43
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
44
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
45
+ declare const _default: typeof __VLS_export;
46
+ export default _default;
47
+ type __VLS_WithSlots<T, S> = T & {
48
+ new (): {
49
+ $slots: S;
50
+ };
51
+ };
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),l=require("vue"),o=require("@embedpdf/core/vue"),n=require("@embedpdf/models"),a=require("@embedpdf/utils/vue"),u=()=>o.useCapability(t.SelectionPlugin.id),i=()=>o.usePlugin(t.SelectionPlugin.id),r=l.defineComponent({__name:"selection-layer",props:{documentId:{},pageIndex:{},scale:{},rotation:{default:n.Rotation.Degree0},background:{default:"rgba(33,150,243)"},selectionMenu:{}},setup(e){const t=e,n=l.useSlots(),{plugin:u}=i(),r=o.useDocumentState(()=>t.documentId),c=l.computed(()=>{var e,l,o;return null==(o=null==(l=null==(e=r.value)?void 0:e.document)?void 0:l.pages)?void 0:o[t.pageIndex]}),d=l.ref([]),s=l.ref(null),p=l.ref(null),v=l.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=r.value)?void 0:e.scale)??1}),g=l.computed(()=>{var e,l;if(void 0!==t.rotation)return t.rotation;return(((null==(e=c.value)?void 0:e.rotation)??0)+((null==(l=r.value)?void 0:l.rotation)??0))%4}),m=l.computed(()=>!!p.value&&(p.value.pageIndex===t.pageIndex&&(!!p.value.isVisible&&(!!t.selectionMenu||!!n["selection-menu"]))));l.watch([()=>u.value,()=>t.documentId,()=>t.pageIndex],([e,t,l],o,n)=>{if(!e||!t)return d.value=[],void(s.value=null);n(e.registerSelectionOnPage({documentId:t,pageIndex:l,onRectsChange:({rects:e,boundingRect:t})=>{d.value=e,s.value=t}}))},{immediate:!0}),l.watch([()=>u.value,()=>t.documentId],([e,t],l,o)=>{if(!e||!t)return void(p.value=null);o(e.onMenuPlacement(t,e=>{p.value=e}))},{immediate:!0});const x=()=>({type:"selection",pageIndex:t.pageIndex}),b=()=>{var e,t,l;return{suggestTop:(null==(e=p.value)?void 0:e.suggestTop)??!1,spaceAbove:(null==(t=p.value)?void 0:t.spaceAbove)??0,spaceBelow:(null==(l=p.value)?void 0:l.spaceBelow)??0}},y=(e,l)=>t.selectionMenu?t.selectionMenu({rect:e,menuWrapperProps:l,selected:!0,placement:b(),context:x()}):null;return(t,o)=>s.value?(l.openBlock(),l.createElementBlock(l.Fragment,{key:0},[l.createElementVNode("div",{style:l.normalizeStyle({position:"absolute",left:s.value.origin.x*v.value+"px",top:s.value.origin.y*v.value+"px",width:s.value.size.width*v.value+"px",height:s.value.size.height*v.value+"px",mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"})},[(l.openBlock(!0),l.createElementBlock(l.Fragment,null,l.renderList(d.value,(t,o)=>(l.openBlock(),l.createElementBlock("div",{key:o,style:l.normalizeStyle({position:"absolute",left:(t.origin.x-s.value.origin.x)*v.value+"px",top:(t.origin.y-s.value.origin.y)*v.value+"px",width:t.size.width*v.value+"px",height:t.size.height*v.value+"px",background:e.background})},null,4))),128))],4),m.value?(l.openBlock(),l.createBlock(l.unref(a.CounterRotate),{key:0,rect:{origin:{x:p.value.rect.origin.x*v.value,y:p.value.rect.origin.y*v.value},size:{width:p.value.rect.size.width*v.value,height:p.value.rect.size.height*v.value}},rotation:g.value},{default:l.withCtx(({rect:o,menuWrapperProps:n})=>[e.selectionMenu?(l.openBlock(),l.createBlock(l.resolveDynamicComponent(y(o,n)),{key:0})):l.renderSlot(t.$slots,"selection-menu",{key:1,context:x(),selected:!0,rect:o,placement:b(),menuWrapperProps:n})]),_:3},8,["rect","rotation"])):l.createCommentVNode("",!0)],64)):l.createCommentVNode("",!0)}}),c=l.defineComponent({__name:"copy-to-clipboard",setup(e){const{provides:t}=u();return l.watchEffect(e=>{if(t.value){e(t.value.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e).catch(e=>{console.error("Failed to copy text to clipboard:",e)})}))}}),(e,t)=>null}}),d=l.defineComponent({__name:"marquee-selection",props:{documentId:{},pageIndex:{},scale:{},className:{},stroke:{default:"rgba(0,122,204,0.8)"},fill:{default:"rgba(0,122,204,0.15)"}},setup(e){const t=e,{plugin:n}=i(),a=o.useDocumentState(()=>t.documentId),u=l.ref(null),r=l.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=a.value)?void 0:e.scale)??1});return l.watch([n,()=>t.documentId,()=>t.pageIndex,r],([e,t,l,o],n,a)=>{if(u.value=null,!e)return;const i=e.registerMarqueeOnPage({documentId:t,pageIndex:l,scale:o,onRectChange:e=>{u.value=e}});a(()=>{null==i||i()})},{immediate:!0}),(t,o)=>u.value?(l.openBlock(),l.createElementBlock("div",{key:0,style:l.normalizeStyle({position:"absolute",pointerEvents:"none",left:u.value.origin.x*r.value+"px",top:u.value.origin.y*r.value+"px",width:u.value.size.width*r.value+"px",height:u.value.size.height*r.value+"px",border:`1px dashed ${e.stroke}`,background:e.fill,boxSizing:"border-box",zIndex:1e3}),class:l.normalizeClass(e.className)},null,6)):l.createCommentVNode("",!0)}}),s=e.createPluginPackage(t.SelectionPluginPackage).addUtility(c).build();exports.CopyToClipboard=c,exports.MarqueeSelection=d,exports.SelectionLayer=r,exports.SelectionPluginPackage=s,exports.useSelectionCapability=u,exports.useSelectionPlugin=i,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/plugin-selection"),o=require("vue"),l=require("@embedpdf/models"),n=require("@embedpdf/core/vue"),a=require("@embedpdf/utils/vue"),r=()=>n.useCapability(t.SelectionPlugin.id),u=()=>n.usePlugin(t.SelectionPlugin.id),i=o.defineComponent({__name:"text-selection",props:{documentId:{},pageIndex:{},scale:{},rotation:{default:l.Rotation.Degree0},background:{default:"rgba(33,150,243)"},selectionMenu:{}},setup(e){const t=e,l=o.useSlots(),{plugin:r}=u(),i=n.useDocumentState(()=>t.documentId),c=o.computed(()=>{var e,o,l;return null==(l=null==(o=null==(e=i.value)?void 0:e.document)?void 0:o.pages)?void 0:l[t.pageIndex]}),d=o.ref([]),s=o.ref(null),p=o.ref(null),v=o.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=i.value)?void 0:e.scale)??1}),m=o.computed(()=>{var e,o;if(void 0!==t.rotation)return t.rotation;return(((null==(e=c.value)?void 0:e.rotation)??0)+((null==(o=i.value)?void 0:o.rotation)??0))%4}),g=o.computed(()=>!!p.value&&(p.value.pageIndex===t.pageIndex&&(!!p.value.isVisible&&(!!t.selectionMenu||!!l["selection-menu"]))));o.watch([()=>r.value,()=>t.documentId,()=>t.pageIndex],([e,t,o],l,n)=>{if(!e||!t)return d.value=[],void(s.value=null);n(e.registerSelectionOnPage({documentId:t,pageIndex:o,onRectsChange:({rects:e,boundingRect:t})=>{d.value=e,s.value=t}}))},{immediate:!0}),o.watch([()=>r.value,()=>t.documentId],([e,t],o,l)=>{if(!e||!t)return void(p.value=null);l(e.onMenuPlacement(t,e=>{p.value=e}))},{immediate:!0});const x=()=>({type:"selection",pageIndex:t.pageIndex}),b=()=>{var e,t,o;return{suggestTop:(null==(e=p.value)?void 0:e.suggestTop)??!1,spaceAbove:(null==(t=p.value)?void 0:t.spaceAbove)??0,spaceBelow:(null==(o=p.value)?void 0:o.spaceBelow)??0}},y=(e,o)=>t.selectionMenu?t.selectionMenu({rect:e,menuWrapperProps:o,selected:!0,placement:b(),context:x()}):null;return(t,l)=>s.value?(o.openBlock(),o.createElementBlock(o.Fragment,{key:0},[o.createElementVNode("div",{style:o.normalizeStyle({position:"absolute",left:s.value.origin.x*v.value+"px",top:s.value.origin.y*v.value+"px",width:s.value.size.width*v.value+"px",height:s.value.size.height*v.value+"px",mixBlendMode:"multiply",isolation:"isolate",pointerEvents:"none"})},[(o.openBlock(!0),o.createElementBlock(o.Fragment,null,o.renderList(d.value,(t,l)=>(o.openBlock(),o.createElementBlock("div",{key:l,style:o.normalizeStyle({position:"absolute",left:(t.origin.x-s.value.origin.x)*v.value+"px",top:(t.origin.y-s.value.origin.y)*v.value+"px",width:t.size.width*v.value+"px",height:t.size.height*v.value+"px",background:e.background})},null,4))),128))],4),g.value?(o.openBlock(),o.createBlock(o.unref(a.CounterRotate),{key:0,rect:{origin:{x:p.value.rect.origin.x*v.value,y:p.value.rect.origin.y*v.value},size:{width:p.value.rect.size.width*v.value,height:p.value.rect.size.height*v.value}},rotation:m.value},{default:o.withCtx(({rect:l,menuWrapperProps:n})=>[e.selectionMenu?(o.openBlock(),o.createBlock(o.resolveDynamicComponent(y(l,n)),{key:0})):o.renderSlot(t.$slots,"selection-menu",{key:1,context:x(),selected:!0,rect:l,placement:b(),menuWrapperProps:n})]),_:3},8,["rect","rotation"])):o.createCommentVNode("",!0)],64)):o.createCommentVNode("",!0)}}),c=o.defineComponent({__name:"marquee-selection",props:{documentId:{},pageIndex:{},scale:{},className:{},background:{},borderColor:{},borderStyle:{default:"dashed"},stroke:{},fill:{}},setup(e){const t=e,l=o.computed(()=>t.borderColor??t.stroke??"rgba(0,122,204,0.8)"),a=o.computed(()=>t.background??t.fill??"rgba(0,122,204,0.15)"),r=o.computed(()=>t.borderStyle),{plugin:i}=u(),c=n.useDocumentState(()=>t.documentId),d=o.ref(null),s=o.computed(()=>{var e;return void 0!==t.scale?t.scale:(null==(e=c.value)?void 0:e.scale)??1});return o.watch([i,()=>t.documentId,()=>t.pageIndex,s],([e,t,o,l],n,a)=>{if(d.value=null,!e)return;const r=e.registerMarqueeOnPage({documentId:t,pageIndex:o,scale:l,onRectChange:e=>{d.value=e}});a(()=>{null==r||r()})},{immediate:!0}),(t,n)=>d.value?(o.openBlock(),o.createElementBlock("div",{key:0,style:o.normalizeStyle({position:"absolute",pointerEvents:"none",left:d.value.origin.x*s.value+"px",top:d.value.origin.y*s.value+"px",width:d.value.size.width*s.value+"px",height:d.value.size.height*s.value+"px",border:`1px ${r.value} ${l.value}`,background:a.value,boxSizing:"border-box",zIndex:1e3}),class:o.normalizeClass(e.className)},null,6)):o.createCommentVNode("",!0)}}),d=o.defineComponent({__name:"selection-layer",props:{documentId:{},pageIndex:{},scale:{},rotation:{default:l.Rotation.Degree0},background:{},textStyle:{},marqueeStyle:{},marqueeClassName:{},selectionMenu:{}},setup(e){const t=e,l=o.computed(()=>{var e;return(null==(e=t.textStyle)?void 0:e.background)??t.background});return(t,n)=>{var a,r,u;return o.openBlock(),o.createElementBlock(o.Fragment,null,[o.createVNode(i,{"document-id":e.documentId,"page-index":e.pageIndex,scale:e.scale,rotation:e.rotation,background:l.value,"selection-menu":e.selectionMenu},{"selection-menu":o.withCtx(e=>[o.renderSlot(t.$slots,"selection-menu",o.normalizeProps(o.guardReactiveProps(e)))]),_:3},8,["document-id","page-index","scale","rotation","background","selection-menu"]),o.createVNode(c,{"document-id":e.documentId,"page-index":e.pageIndex,scale:e.scale,background:null==(a=e.marqueeStyle)?void 0:a.background,"border-color":null==(r=e.marqueeStyle)?void 0:r.borderColor,"border-style":null==(u=e.marqueeStyle)?void 0:u.borderStyle,"class-name":e.marqueeClassName},null,8,["document-id","page-index","scale","background","border-color","border-style","class-name"])],64)}}}),s=o.defineComponent({__name:"copy-to-clipboard",setup(e){const{provides:t}=r();return o.watchEffect(e=>{if(t.value){e(t.value.onCopyToClipboard(({text:e})=>{navigator.clipboard.writeText(e).catch(e=>{console.error("Failed to copy text to clipboard:",e)})}))}}),(e,t)=>null}}),p=e.createPluginPackage(t.SelectionPluginPackage).addUtility(s).build();exports.CopyToClipboard=s,exports.MarqueeSelection=c,exports.SelectionLayer=d,exports.SelectionPluginPackage=p,exports.TextSelection=i,exports.useSelectionCapability=r,exports.useSelectionPlugin=u,Object.keys(t).forEach(e=>{"default"===e||Object.prototype.hasOwnProperty.call(exports,e)||Object.defineProperty(exports,e,{enumerable:!0,get:()=>t[e]})});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-selection.ts","../../src/vue/components/selection-layer.vue","../../src/vue/components/copy-to-clipboard.vue","../../src/vue/components/marquee-selection.vue","../../src/vue/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script setup lang=\"ts\">\nimport { ref, watch, computed, useSlots, type VNode } from 'vue';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { Rotation, type Rect } from '@embedpdf/models';\nimport type { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { CounterRotate, type MenuWrapperProps } from '@embedpdf/utils/vue';\nimport { useSelectionPlugin } from '../hooks/use-selection';\nimport type { SelectionSelectionContext, SelectionSelectionMenuRenderFn } from '../types';\n\ninterface SelectionLayerProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n}\n\nconst props = withDefaults(defineProps<SelectionLayerProps>(), {\n background: 'rgba(33,150,243)',\n rotation: Rotation.Degree0,\n});\n\nconst slots = useSlots();\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst page = computed(() => documentState.value?.document?.pages?.[props.pageIndex]);\nconst rects = ref<Rect[]>([]);\nconst boundingRect = ref<Rect | null>(null);\nconst placement = ref<SelectionMenuPlacement | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nconst actualRotation = computed(() => {\n if (props.rotation !== undefined) return props.rotation;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page.value?.rotation ?? 0;\n const docRotation = documentState.value?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n});\n\n// Check if menu should render: placement is valid AND (render fn OR slot exists)\nconst shouldRenderMenu = computed(() => {\n if (!placement.value) return false;\n if (placement.value.pageIndex !== props.pageIndex) return false;\n if (!placement.value.isVisible) return false;\n\n // Must have either render function or slot\n return !!props.selectionMenu || !!slots['selection-menu'];\n});\n\nwatch(\n [() => selPlugin.value, () => props.documentId, () => props.pageIndex],\n ([plugin, docId, pageIdx], _, onCleanup) => {\n if (!plugin || !docId) {\n rects.value = [];\n boundingRect.value = null;\n return;\n }\n\n const unregister = plugin.registerSelectionOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects.value = newRects;\n boundingRect.value = newBoundingRect;\n },\n });\n\n onCleanup(unregister);\n },\n { immediate: true },\n);\n\nwatch(\n [() => selPlugin.value, () => props.documentId],\n ([plugin, docId], _, onCleanup) => {\n if (!plugin || !docId) {\n placement.value = null;\n return;\n }\n\n const unsubscribe = plugin.onMenuPlacement(docId, (newPlacement) => {\n placement.value = newPlacement;\n });\n\n onCleanup(unsubscribe);\n },\n { immediate: true },\n);\n\n// --- Selection Menu Logic ---\n\n// Build context object for selection menu\nconst buildContext = (): SelectionSelectionContext => ({\n type: 'selection',\n pageIndex: props.pageIndex,\n});\n\n// Build placement hints from plugin placement data\nconst buildMenuPlacement = () => ({\n suggestTop: placement.value?.suggestTop ?? false,\n spaceAbove: placement.value?.spaceAbove ?? 0,\n spaceBelow: placement.value?.spaceBelow ?? 0,\n});\n\n// Render via function (for schema-driven approach)\nconst renderSelectionMenu = (rect: Rect, menuWrapperProps: MenuWrapperProps): VNode | null => {\n if (!props.selectionMenu) return null;\n\n return props.selectionMenu({\n rect,\n menuWrapperProps,\n selected: true, // Selection is always \"selected\" when visible\n placement: buildMenuPlacement(),\n context: buildContext(),\n });\n};\n</script>\n\n<template>\n <template v-if=\"boundingRect\">\n <div\n :style=\"{\n position: 'absolute',\n left: `${boundingRect.origin.x * actualScale}px`,\n top: `${boundingRect.origin.y * actualScale}px`,\n width: `${boundingRect.size.width * actualScale}px`,\n height: `${boundingRect.size.height * actualScale}px`,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }\"\n >\n <div\n v-for=\"(rect, i) in rects\"\n :key=\"i\"\n :style=\"{\n position: 'absolute',\n left: `${(rect.origin.x - boundingRect.origin.x) * actualScale}px`,\n top: `${(rect.origin.y - boundingRect.origin.y) * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n background: background,\n }\"\n />\n </div>\n\n <!-- Selection Menu: Supports BOTH render function and slot -->\n <CounterRotate\n v-if=\"shouldRenderMenu\"\n :rect=\"{\n origin: {\n x: placement!.rect.origin.x * actualScale,\n y: placement!.rect.origin.y * actualScale,\n },\n size: {\n width: placement!.rect.size.width * actualScale,\n height: placement!.rect.size.height * actualScale,\n },\n }\"\n :rotation=\"actualRotation\"\n >\n <template #default=\"{ rect, menuWrapperProps }\">\n <!-- Priority 1: Render function prop (schema-driven) -->\n <component v-if=\"selectionMenu\" :is=\"renderSelectionMenu(rect, menuWrapperProps)\" />\n\n <!-- Priority 2: Slot (manual customization) -->\n <slot\n v-else\n name=\"selection-menu\"\n :context=\"buildContext()\"\n :selected=\"true\"\n :rect=\"rect\"\n :placement=\"buildMenuPlacement()\"\n :menuWrapperProps=\"menuWrapperProps\"\n />\n </template>\n </CounterRotate>\n </template>\n</template>\n","<script setup lang=\"ts\">\nimport { watchEffect } from 'vue';\nimport { useSelectionCapability } from '../hooks';\n\nconst { provides: sel } = useSelectionCapability();\n\n// This effect runs when the component is mounted and the capability is available.\n// It automatically handles unsubscribing when the component is unmounted.\nwatchEffect((onCleanup) => {\n if (sel.value) {\n const unsubscribe = sel.value.onCopyToClipboard(({ text }) => {\n // Use the Clipboard API to write the text\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n\n // Register the cleanup function to run on unmount or re-run\n onCleanup(unsubscribe);\n }\n});\n</script>\n\n<template>\n <!-- This component renders nothing to the DOM -->\n</template>\n","<template>\n <div\n v-if=\"rect\"\n :style=\"{\n position: 'absolute',\n pointerEvents: 'none',\n left: `${rect.origin.x * actualScale}px`,\n top: `${rect.origin.y * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n border: `1px dashed ${stroke}`,\n background: fill,\n boxSizing: 'border-box',\n zIndex: 1000,\n }\"\n :class=\"className\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Stroke colour (default: 'rgba(0,122,204,0.8)') */\n stroke?: string;\n /** Fill colour (default: 'rgba(0,122,204,0.15)') */\n fill?: string;\n}\n\nconst props = withDefaults(defineProps<MarqueeSelectionProps>(), {\n stroke: 'rgba(0,122,204,0.8)',\n fill: 'rgba(0,122,204,0.15)',\n});\n\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rect = ref<Rect | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nwatch(\n [selPlugin, () => props.documentId, () => props.pageIndex, actualScale],\n ([plugin, docId, pageIdx, scale], _, onCleanup) => {\n rect.value = null;\n\n if (!plugin) {\n return;\n }\n\n const unregister = plugin.registerMarqueeOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n scale,\n onRectChange: (newRect) => {\n rect.value = newRect;\n },\n });\n\n onCleanup(() => {\n unregister?.();\n });\n },\n { immediate: true },\n);\n</script>\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","props","__props","slots","useSlots","plugin","selPlugin","documentState","useDocumentState","documentId","page","computed","_c","_b","_a","value","document","pages","pageIndex","rects","ref","boundingRect","placement","actualScale","scale","actualRotation","rotation","shouldRenderMenu","isVisible","selectionMenu","watch","docId","pageIdx","_","onCleanup","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","immediate","onMenuPlacement","newPlacement","buildContext","type","buildMenuPlacement","suggestTop","spaceAbove","spaceBelow","renderSelectionMenu","rect","menuWrapperProps","selected","context","_createElementBlock","_Fragment","key","_createElementVNode","style","_normalizeStyle","left","origin","x","top","y","width","size","height","_openBlock","_renderList","i","background","_createBlock","_unref","CounterRotate","default","_withCtx","_resolveDynamicComponent","_renderSlot","_ctx","$slots","provides","sel","watchEffect","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","unregister","registerMarqueeOnPage","onRectChange","newRect","stroke","fill","class","className","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/vue/hooks/use-selection.ts","../../src/vue/components/text-selection.vue","../../src/vue/components/marquee-selection.vue","../../src/vue/components/selection-layer.vue","../../src/vue/components/copy-to-clipboard.vue","../../src/vue/index.ts"],"sourcesContent":["import { useCapability, usePlugin } from '@embedpdf/core/vue';\nimport { SelectionPlugin } from '@embedpdf/plugin-selection';\n\n/**\n * Hook to get the selection plugin's capability API.\n * This provides methods for controlling and listening to selection events.\n */\nexport const useSelectionCapability = () => useCapability<SelectionPlugin>(SelectionPlugin.id);\n\n/**\n * Hook to get the raw selection plugin instance.\n * Useful for accessing plugin-specific properties or methods not exposed in the capability.\n */\nexport const useSelectionPlugin = () => usePlugin<SelectionPlugin>(SelectionPlugin.id);\n","<script setup lang=\"ts\">\nimport { ref, watch, computed, useSlots, type VNode } from 'vue';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { Rotation, type Rect } from '@embedpdf/models';\nimport type { SelectionMenuPlacement } from '@embedpdf/plugin-selection';\nimport { CounterRotate, type MenuWrapperProps } from '@embedpdf/utils/vue';\nimport { useSelectionPlugin } from '../hooks/use-selection';\nimport type { SelectionSelectionContext, SelectionSelectionMenuRenderFn } from '../types';\n\ninterface TextSelectionProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n /** Background color for text selection highlights. Default: 'rgba(33,150,243)' */\n background?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n}\n\nconst props = withDefaults(defineProps<TextSelectionProps>(), {\n background: 'rgba(33,150,243)',\n rotation: Rotation.Degree0,\n});\n\nconst slots = useSlots();\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst page = computed(() => documentState.value?.document?.pages?.[props.pageIndex]);\nconst rects = ref<Rect[]>([]);\nconst boundingRect = ref<Rect | null>(null);\nconst placement = ref<SelectionMenuPlacement | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nconst actualRotation = computed(() => {\n if (props.rotation !== undefined) return props.rotation;\n // Combine page intrinsic rotation with document rotation\n const pageRotation = page.value?.rotation ?? 0;\n const docRotation = documentState.value?.rotation ?? 0;\n return ((pageRotation + docRotation) % 4) as Rotation;\n});\n\n// Check if menu should render: placement is valid AND (render fn OR slot exists)\nconst shouldRenderMenu = computed(() => {\n if (!placement.value) return false;\n if (placement.value.pageIndex !== props.pageIndex) return false;\n if (!placement.value.isVisible) return false;\n\n // Must have either render function or slot\n return !!props.selectionMenu || !!slots['selection-menu'];\n});\n\nwatch(\n [() => selPlugin.value, () => props.documentId, () => props.pageIndex],\n ([plugin, docId, pageIdx], _, onCleanup) => {\n if (!plugin || !docId) {\n rects.value = [];\n boundingRect.value = null;\n return;\n }\n\n const unregister = plugin.registerSelectionOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n onRectsChange: ({ rects: newRects, boundingRect: newBoundingRect }) => {\n rects.value = newRects;\n boundingRect.value = newBoundingRect;\n },\n });\n\n onCleanup(unregister);\n },\n { immediate: true },\n);\n\nwatch(\n [() => selPlugin.value, () => props.documentId],\n ([plugin, docId], _, onCleanup) => {\n if (!plugin || !docId) {\n placement.value = null;\n return;\n }\n\n const unsubscribe = plugin.onMenuPlacement(docId, (newPlacement) => {\n placement.value = newPlacement;\n });\n\n onCleanup(unsubscribe);\n },\n { immediate: true },\n);\n\n// --- Selection Menu Logic ---\n\n// Build context object for selection menu\nconst buildContext = (): SelectionSelectionContext => ({\n type: 'selection',\n pageIndex: props.pageIndex,\n});\n\n// Build placement hints from plugin placement data\nconst buildMenuPlacement = () => ({\n suggestTop: placement.value?.suggestTop ?? false,\n spaceAbove: placement.value?.spaceAbove ?? 0,\n spaceBelow: placement.value?.spaceBelow ?? 0,\n});\n\n// Render via function (for schema-driven approach)\nconst renderSelectionMenu = (rect: Rect, menuWrapperProps: MenuWrapperProps): VNode | null => {\n if (!props.selectionMenu) return null;\n\n return props.selectionMenu({\n rect,\n menuWrapperProps,\n selected: true, // Selection is always \"selected\" when visible\n placement: buildMenuPlacement(),\n context: buildContext(),\n });\n};\n</script>\n\n<template>\n <template v-if=\"boundingRect\">\n <div\n :style=\"{\n position: 'absolute',\n left: `${boundingRect.origin.x * actualScale}px`,\n top: `${boundingRect.origin.y * actualScale}px`,\n width: `${boundingRect.size.width * actualScale}px`,\n height: `${boundingRect.size.height * actualScale}px`,\n mixBlendMode: 'multiply',\n isolation: 'isolate',\n pointerEvents: 'none',\n }\"\n >\n <div\n v-for=\"(rect, i) in rects\"\n :key=\"i\"\n :style=\"{\n position: 'absolute',\n left: `${(rect.origin.x - boundingRect.origin.x) * actualScale}px`,\n top: `${(rect.origin.y - boundingRect.origin.y) * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n background: background,\n }\"\n />\n </div>\n\n <!-- Selection Menu: Supports BOTH render function and slot -->\n <CounterRotate\n v-if=\"shouldRenderMenu\"\n :rect=\"{\n origin: {\n x: placement!.rect.origin.x * actualScale,\n y: placement!.rect.origin.y * actualScale,\n },\n size: {\n width: placement!.rect.size.width * actualScale,\n height: placement!.rect.size.height * actualScale,\n },\n }\"\n :rotation=\"actualRotation\"\n >\n <template #default=\"{ rect, menuWrapperProps }\">\n <!-- Priority 1: Render function prop (schema-driven) -->\n <component v-if=\"selectionMenu\" :is=\"renderSelectionMenu(rect, menuWrapperProps)\" />\n\n <!-- Priority 2: Slot (manual customization) -->\n <slot\n v-else\n name=\"selection-menu\"\n :context=\"buildContext()\"\n :selected=\"true\"\n :rect=\"rect\"\n :placement=\"buildMenuPlacement()\"\n :menuWrapperProps=\"menuWrapperProps\"\n />\n </template>\n </CounterRotate>\n </template>\n</template>\n","<template>\n <div\n v-if=\"rect\"\n :style=\"{\n position: 'absolute',\n pointerEvents: 'none',\n left: `${rect.origin.x * actualScale}px`,\n top: `${rect.origin.y * actualScale}px`,\n width: `${rect.size.width * actualScale}px`,\n height: `${rect.size.height * actualScale}px`,\n border: `1px ${resolvedBorderStyle} ${resolvedBorderColor}`,\n background: resolvedBackground,\n boxSizing: 'border-box',\n zIndex: 1000,\n }\"\n :class=\"className\"\n />\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from 'vue';\nimport type { Rect } from '@embedpdf/models';\nimport { useDocumentState } from '@embedpdf/core/vue';\nimport { useSelectionPlugin } from '../hooks';\n\ninterface MarqueeSelectionProps {\n /** The ID of the document */\n documentId: string;\n /** Index of the page this layer lives on */\n pageIndex: number;\n /** Scale of the page (optional, defaults to document scale) */\n scale?: number;\n /** Optional CSS class applied to the marquee rectangle */\n className?: string;\n /** Fill/background color inside the marquee rectangle. Default: 'rgba(0,122,204,0.15)' */\n background?: string;\n /** Border color of the marquee rectangle. Default: 'rgba(0,122,204,0.8)' */\n borderColor?: string;\n /** Border style. Default: 'dashed' */\n borderStyle?: 'solid' | 'dashed' | 'dotted';\n /**\n * @deprecated Use `borderColor` instead.\n */\n stroke?: string;\n /**\n * @deprecated Use `background` instead.\n */\n fill?: string;\n}\n\nconst props = withDefaults(defineProps<MarqueeSelectionProps>(), {\n borderStyle: 'dashed',\n});\n\n// Resolve deprecated props: new CSS-standard props take precedence\nconst resolvedBorderColor = computed(\n () => props.borderColor ?? props.stroke ?? 'rgba(0,122,204,0.8)',\n);\nconst resolvedBackground = computed(() => props.background ?? props.fill ?? 'rgba(0,122,204,0.15)');\nconst resolvedBorderStyle = computed(() => props.borderStyle);\n\nconst { plugin: selPlugin } = useSelectionPlugin();\nconst documentState = useDocumentState(() => props.documentId);\nconst rect = ref<Rect | null>(null);\n\nconst actualScale = computed(() => {\n if (props.scale !== undefined) return props.scale;\n return documentState.value?.scale ?? 1;\n});\n\nwatch(\n [selPlugin, () => props.documentId, () => props.pageIndex, actualScale],\n ([plugin, docId, pageIdx, scale], _, onCleanup) => {\n rect.value = null;\n\n if (!plugin) {\n return;\n }\n\n const unregister = plugin.registerMarqueeOnPage({\n documentId: docId,\n pageIndex: pageIdx,\n scale,\n onRectChange: (newRect) => {\n rect.value = newRect;\n },\n });\n\n onCleanup(() => {\n unregister?.();\n });\n },\n { immediate: true },\n);\n</script>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { Rotation } from '@embedpdf/models';\nimport type { TextSelectionStyle, MarqueeSelectionStyle } from '@embedpdf/plugin-selection';\nimport type { SelectionSelectionMenuRenderFn } from '../types';\nimport TextSelection from './text-selection.vue';\nimport MarqueeSelection from './marquee-selection.vue';\n\ninterface SelectionLayerProps {\n documentId: string;\n pageIndex: number;\n scale?: number;\n rotation?: Rotation;\n /**\n * @deprecated Use `textStyle.background` instead.\n * Background color for selection rectangles.\n */\n background?: string;\n /** Styling options for text selection highlights */\n textStyle?: TextSelectionStyle;\n /** Styling options for the marquee selection rectangle */\n marqueeStyle?: MarqueeSelectionStyle;\n /** Optional CSS class applied to the marquee rectangle */\n marqueeClassName?: string;\n /** Render function for selection menu (schema-driven approach) */\n selectionMenu?: SelectionSelectionMenuRenderFn;\n}\n\nconst props = withDefaults(defineProps<SelectionLayerProps>(), {\n rotation: Rotation.Degree0,\n});\n\nconst resolvedTextBackground = computed(() => props.textStyle?.background ?? props.background);\n</script>\n\n<template>\n <TextSelection\n :document-id=\"documentId\"\n :page-index=\"pageIndex\"\n :scale=\"scale\"\n :rotation=\"rotation\"\n :background=\"resolvedTextBackground\"\n :selection-menu=\"selectionMenu\"\n >\n <template #selection-menu=\"menuProps\">\n <slot name=\"selection-menu\" v-bind=\"menuProps\" />\n </template>\n </TextSelection>\n <MarqueeSelection\n :document-id=\"documentId\"\n :page-index=\"pageIndex\"\n :scale=\"scale\"\n :background=\"marqueeStyle?.background\"\n :border-color=\"marqueeStyle?.borderColor\"\n :border-style=\"marqueeStyle?.borderStyle\"\n :class-name=\"marqueeClassName\"\n />\n</template>\n","<script setup lang=\"ts\">\nimport { watchEffect } from 'vue';\nimport { useSelectionCapability } from '../hooks';\n\nconst { provides: sel } = useSelectionCapability();\n\n// This effect runs when the component is mounted and the capability is available.\n// It automatically handles unsubscribing when the component is unmounted.\nwatchEffect((onCleanup) => {\n if (sel.value) {\n const unsubscribe = sel.value.onCopyToClipboard(({ text }) => {\n // Use the Clipboard API to write the text\n navigator.clipboard.writeText(text).catch((err) => {\n console.error('Failed to copy text to clipboard:', err);\n });\n });\n\n // Register the cleanup function to run on unmount or re-run\n onCleanup(unsubscribe);\n }\n});\n</script>\n\n<template>\n <!-- This component renders nothing to the DOM -->\n</template>\n","import { createPluginPackage } from '@embedpdf/core';\nimport { SelectionPluginPackage as BaseSelectionPluginPackage } from '@embedpdf/plugin-selection';\n\nimport { CopyToClipboard } from './components';\n\nexport * from './hooks';\nexport * from './components';\nexport * from './types';\nexport * from '@embedpdf/plugin-selection';\n\nexport const SelectionPluginPackage = createPluginPackage(BaseSelectionPluginPackage)\n .addUtility(CopyToClipboard)\n .build();\n"],"names":["useSelectionCapability","useCapability","SelectionPlugin","id","useSelectionPlugin","usePlugin","props","__props","slots","useSlots","plugin","selPlugin","documentState","useDocumentState","documentId","page","computed","_c","_b","_a","value","document","pages","pageIndex","rects","ref","boundingRect","placement","actualScale","scale","actualRotation","rotation","shouldRenderMenu","isVisible","selectionMenu","watch","docId","pageIdx","_","onCleanup","registerSelectionOnPage","onRectsChange","newRects","newBoundingRect","immediate","onMenuPlacement","newPlacement","buildContext","type","buildMenuPlacement","suggestTop","spaceAbove","spaceBelow","renderSelectionMenu","rect","menuWrapperProps","selected","context","_createElementBlock","_Fragment","key","_createElementVNode","style","_normalizeStyle","left","origin","x","top","y","width","size","height","_openBlock","_renderList","i","background","_createBlock","_unref","CounterRotate","default","_withCtx","_resolveDynamicComponent","_renderSlot","_ctx","$slots","resolvedBorderColor","borderColor","stroke","resolvedBackground","fill","resolvedBorderStyle","borderStyle","unregister","registerMarqueeOnPage","onRectChange","newRect","border","class","className","resolvedTextBackground","textStyle","_createVNode","TextSelection","menuProps","MarqueeSelection","marqueeStyle","marqueeClassName","provides","sel","watchEffect","onCopyToClipboard","text","navigator","clipboard","writeText","catch","err","console","error","SelectionPluginPackage","createPluginPackage","BaseSelectionPluginPackage","addUtility","CopyToClipboard","build"],"mappings":"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"}