webmarker-js 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,8 @@ Mark web pages for use with vision-language models.
11
11
 
12
12
  ## Overview
13
13
 
14
+ 🚧 Under Construction
15
+
14
16
  **WebMarker** adds visual markings with labels to elements on a web page. This can be used for [Set-of-Mark (SoM)](https://github.com/microsoft/SoM) prompting, which improves visual grounding abilities of vision-language models such as GPT-4V, Claude 3, and Google Gemini 1.5.
15
17
 
16
18
  ## Usage
@@ -30,29 +32,53 @@ console.log(elements.get("0").element);
30
32
  unmark();
31
33
  ```
32
34
 
33
- ### Options
35
+ ## Options
34
36
 
35
- ```javascript
37
+ - **selector**: A custom CSS selector to specify which elements to mark.
38
+ - Type: `string`
39
+ - Default: `"button, input, a, select, textarea"`
40
+ - **markStyle**: A CSS style to apply to the label element. You can also specify a function that returns a CSS style object.
41
+ - Type: `Readonly<Partial<CSSStyleDeclaration>> or (element: Element) => Readonly<Partial<CSSStyleDeclaration>>`
42
+ - Default: `{backgroundColor: "red", color: "white", padding: "2px 4px", fontSize: "12px", fontWeight: "bold"}`
43
+ - **markPlacement**: The placement of the mark relative to the element.
44
+ - Type: `'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end'`
45
+ - Default: `'top-start'`
46
+ - **maskStyle**: A CSS style to apply to the bounding box element. You can also specify a function that returns a CSS style object. Bounding boxes are only shown if showMasks is true.
47
+ - Type: `Readonly<Partial<CSSStyleDeclaration>> or (element: Element) => Readonly<Partial<CSSStyleDeclaration>>`
48
+ - Default: `{outline: "2px dashed red", backgroundColor: "transparent"}`
49
+ - **showMasks**: Whether or not to show bounding boxes around the elements.
50
+ - Type: `boolean`
51
+ - Default: `true`
52
+ - **labelGenerator**: Provide a function for generating labels. By default, labels are generated as numbers starting from 0.
53
+ - Type: `(element: Element, index: number) => string`
54
+ - Default: `(_, index) => index.toString()`
55
+ - **containerElement**: Provide a container element to query the elements to be marked. By default, the container element is document.body.
56
+ - Type: `Element`
57
+ - Default: `document.body`
58
+ - **viewPortOnly**: Only mark elements that are visible in the current viewport.
59
+ - Type: `boolean`
60
+ - Default: `false`
61
+
62
+ ### Advanced example
63
+
64
+ ```typescript
36
65
  let elements = mark({
37
- // A custom CSS selector for which elements to mark.
38
- // The default selector will select all interactive elements.
66
+ // Only mark buttons and inputs
39
67
  selector: "button, input",
40
- // A custom CSS style to apply to the mark element.
68
+ // Use a blue mark with white text
41
69
  markStyle: { color: "white", backgroundColor: "blue", padding: 5 },
42
- // A custom CSS style to apply to the mask element.
43
- // Use a transparent background to have it act as a bounding box.
44
- maskStyle: { outline: "2px dashed blue", backgroundColor: "transparent" },
45
- // The placement of the mark relative to the element.
46
- markPlacement: "top-start";
47
- // Whether or not to show masks over elements.
48
- // Defaults to true.
70
+ // Use a blue dashed outline mask with a transparent and slighly blue background
71
+ maskStyle: { outline: "2px dashed blue", backgroundColor: "rgba(0, 0, 255, 0.1)"},
72
+ // Place the mark at the top right corner of the element
73
+ markPlacement: "top-end";
74
+ // Show masks over elements (defaults to true)
49
75
  showMasks: true,
50
- // A custom function for generating the labels.
76
+ // Generate labels as 'Element 0', 'Element 1', 'Element 2'...
51
77
  // Defaults to '0', '1', '2'... if not provided.
52
78
  labelGenerator: (element, index) => `Element ${index}`,
53
- // A container element to query the elements to be marked.
54
- // Defaults to the document.
55
- containerElement: document.body,
79
+ // A custom container element to query the elements to be marked.
80
+ // Defaults to the document.body.
81
+ containerElement: document.body.querySelector("main"),
56
82
  // Only mark elements that are visible in the current viewport
57
83
  viewPortOnly: true,
58
84
  });
package/dist/main.js CHANGED
@@ -300,6 +300,9 @@ function $fd046e7a82b9f872$export$3522532a010241a0() {
300
300
  function $fd046e7a82b9f872$export$3b489baf08eae67a() {
301
301
  return document.documentElement.hasAttribute("data-webmarkered");
302
302
  }
303
+ window["mark"] = $fd046e7a82b9f872$export$bf7f2fce5c1cf636;
304
+ window["unmark"] = $fd046e7a82b9f872$export$3522532a010241a0;
305
+ window["isMarked"] = $fd046e7a82b9f872$export$3b489baf08eae67a;
303
306
 
304
307
 
305
308
  //# sourceMappingURL=main.js.map
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"mappings":";;;;;;;;;;;A,I,kC,a,U,S,I,S,O,E,U,E,C,E,S;I,S,M,K;Q,O,iB,I,Q,I,E,S,O;Y,Q;Q;I;I,O,I,C,K,C,I,O,C,E,S,O,E,M;Q,S,U,K;Y,I;gB,K,U,I,C;Y,E,O,G;gB,O;Y;Q;Q,S,S,K;Y,I;gB,K,S,C,Q,C;Y,E,O,G;gB,O;Y;Q;Q,S,K,M;Y,O,I,G,Q,O,K,I,M,O,K,E,I,C,W;Q;Q,K,A,C,Y,U,K,C,S,c,E,C,E,I;I;A;A,I,oC,a,U,W,I,S,O,E,I;I,I,I;Q,O;Q,M;Y,I,C,C,E,G,G,M,C,C,E;Y,O,C,C,E;Q;Q,M,E;Q,K,E;I,G,G,G,G;I,O,I;Q,M,K;Q,S,K;Q,U,K;I,G,O,W,c,C,C,C,O,Q,C,G;Q,O,I;I,C,G;I,S,K,C;Q,O,S,C;Y,O,K;gB;gB;a;Q;I;I,S,K,E;Q,I,G,M,I,U;Q,M,K,C,I,G,E,C,E,I,C,I,C,C,G,E,I;Y,I,I,G,K,C,I,E,C,E,G,I,C,C,S,G,E,C,E,G,C,C,Q,I,C,A,C,I,C,C,S,A,K,E,I,C,I,C,I,E,I,A,K,C,A,C,I,E,I,C,G,E,C,E,C,E,I,E,O;Y,I,I,G,G,K;gB,E,C,E,G;gB,E,K;a;Y,O,E,C,E;gB,K;gB,K;oB,I;oB;gB,K;oB,E,K;oB,O;wB,O,E,C,E;wB,M;oB;gB,K;oB,E,K;oB,I,E,C,E;oB,K;wB;qB;oB;gB,K;oB,K,E,G,C,G;oB,E,I,C,G;oB;gB;oB,I,C,C,I,E,I,E,I,E,M,G,K,C,C,E,M,G,E,A,K,C,E,C,E,K,K,E,C,E,K,C,G;wB,I;wB;oB;oB,I,E,C,E,K,K,C,C,K,E,C,E,G,C,C,E,I,E,C,E,G,C,C,E,G;wB,E,K,G,E,C,E;wB;oB;oB,I,E,C,E,K,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,I;wB;oB;oB,I,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,E,G,C,I,C;wB;oB;oB,I,C,C,E,E,E,G,C,G;oB,E,I,C,G;oB;Y;Y,K,K,I,C,S;Q,E,O,G;Y,K;gB;gB;a;Y,I;Q,S;Y,I,I;Q;Q,I,E,C,E,G,G,M,E,C,E;Q,O;Y,O,E,C,E,G,E,C,E,G,K;Y,M;Q;I;A;AAwEA,IAA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","sources":["src/index.ts"],"sourcesContent":["import { autoUpdate, computePosition } from \"@floating-ui/dom\";\n\ntype Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"right\"\n | \"right-start\"\n | \"right-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"left-start\"\n | \"left-end\";\n\ninterface MarkOptions {\n /**\n * A CSS selector to specify the elements to be marked.\n */\n selector?: string;\n /**\n * A CSS style to apply to the label element.\n * You can also specify a function that returns a CSS style object.\n */\n markStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * The placement of the mark relative to the element.\n *\n * @default 'top-start'\n */\n markPlacement?: Placement;\n /**\n * A CSS style to apply to the bounding box element.\n * You can also specify a function that returns a CSS style object.\n * Bounding boxes are only shown if `showMasks` is `true`.\n */\n maskStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * Whether or not to show bounding boxes around the elements.\n *\n * @default true\n */\n showMasks?: boolean;\n /**\n * Provide a function for generating labels.\n * By default, labels are generated as numbers starting from 0.\n */\n labelGenerator?: (element: Element, index: number) => string;\n /**\n * Provide a container element to query the elements to be marked.\n * By default, the container element is `document.body`.\n */\n containerElement?: Element;\n /**\n * Only mark elements that are visible in the current viewport\n *\n * @default false\n */\n viewPortOnly?: boolean;\n}\n\ninterface MarkedElement {\n element: Element;\n markElement: HTMLElement;\n maskElement?: HTMLElement;\n}\n\nlet cleanupFns: (() => void)[] = [];\n\nasync function mark(\n options: MarkOptions = {}\n): Promise<Map<string, MarkedElement>> {\n const {\n selector = \"button, input, a, select, textarea\",\n markStyle = {\n backgroundColor: \"red\",\n color: \"white\",\n padding: \"2px 4px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n },\n markPlacement = \"top-start\",\n maskStyle = {\n outline: \"2px dashed red\",\n backgroundColor: \"transparent\",\n },\n showMasks = true,\n labelGenerator = (_, index) => index.toString(),\n containerElement = document.body,\n viewPortOnly = false,\n } = options;\n\n const elements = Array.from(\n containerElement.querySelectorAll(selector)\n ).filter(\n (el) => !viewPortOnly || el.getBoundingClientRect().top < window.innerHeight\n );\n\n const markedElements = new Map<string, MarkedElement>();\n\n await Promise.all(\n elements.map(async (element, index) => {\n const label = labelGenerator(element, index);\n const markElement = createMark(element, markStyle, label, markPlacement);\n\n const maskElement = showMasks\n ? createMask(element, maskStyle, label)\n : undefined;\n\n markedElements.set(label, { element, markElement, maskElement });\n element.setAttribute(\"data-webmarkeredby\", `webmarker-${label}`);\n })\n );\n\n document.documentElement.dataset.webmarkered = \"true\";\n return markedElements;\n}\n\nfunction createMark(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string,\n markPlacement: Placement = \"top-start\"\n): HTMLElement {\n const markElement = document.createElement(\"div\");\n markElement.className = \"webmarker\";\n markElement.id = `webmarker-${label}`;\n markElement.textContent = label;\n document.body.appendChild(markElement);\n positionMark(markElement, element, markPlacement);\n applyStyle(\n markElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return markElement;\n}\n\nfunction createMask(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string\n): HTMLElement {\n const maskElement = document.createElement(\"div\");\n maskElement.className = \"webmarkermask\";\n maskElement.id = `webmarkermask-${label}`;\n document.body.appendChild(maskElement);\n positionMask(maskElement, element);\n applyStyle(\n maskElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return maskElement;\n}\n\nfunction positionMark(\n markElement: HTMLElement,\n element: Element,\n markPlacement: Placement\n) {\n async function updatePosition() {\n const { x, y } = await computePosition(element, markElement, {\n placement: markPlacement,\n });\n Object.assign(markElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n cleanupFns.push(autoUpdate(element, markElement, updatePosition));\n}\n\nasync function positionMask(mask: HTMLElement, element: Element) {\n const { width, height } = element.getBoundingClientRect();\n async function updatePosition() {\n const { x: maskX, y: maskY } = await computePosition(element, mask, {\n placement: \"top-start\",\n });\n Object.assign(mask.style, {\n left: `${maskX}px`,\n top: `${maskY + height}px`,\n width: `${width}px`,\n height: `${height}px`,\n });\n }\n cleanupFns.push(autoUpdate(element, mask, updatePosition));\n}\n\nfunction applyStyle(\n element: HTMLElement,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>,\n customStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n Object.assign(element.style, defaultStyle, customStyle);\n}\n\nfunction unmark(): void {\n document\n .querySelectorAll(\".webmarker, .webmarkermask\")\n .forEach((el) => el.remove());\n document.documentElement.removeAttribute(\"data-webmarkered\");\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n}\n\nfunction isMarked(): boolean {\n return document.documentElement.hasAttribute(\"data-webmarkered\");\n}\n\nexport { mark, unmark, isMarked };\nexport type { MarkOptions, MarkedElement, Placement };\n"],"names":[],"version":3,"file":"main.js.map"}
1
+ {"mappings":";;;;;;;;;;;A,I,kC,a,U,S,I,S,O,E,U,E,C,E,S;I,S,M,K;Q,O,iB,I,Q,I,E,S,O;Y,Q;Q;I;I,O,I,C,K,C,I,O,C,E,S,O,E,M;Q,S,U,K;Y,I;gB,K,U,I,C;Y,E,O,G;gB,O;Y;Q;Q,S,S,K;Y,I;gB,K,S,C,Q,C;Y,E,O,G;gB,O;Y;Q;Q,S,K,M;Y,O,I,G,Q,O,K,I,M,O,K,E,I,C,W;Q;Q,K,A,C,Y,U,K,C,S,c,E,C,E,I;I;A;A,I,oC,a,U,W,I,S,O,E,I;I,I,I;Q,O;Q,M;Y,I,C,C,E,G,G,M,C,C,E;Y,O,C,C,E;Q;Q,M,E;Q,K,E;I,G,G,G,G;I,O,I;Q,M,K;Q,S,K;Q,U,K;I,G,O,W,c,C,C,C,O,Q,C,G;Q,O,I;I,C,G;I,S,K,C;Q,O,S,C;Y,O,K;gB;gB;a;Q;I;I,S,K,E;Q,I,G,M,I,U;Q,M,K,C,I,G,E,C,E,I,C,I,C,C,G,E,I;Y,I,I,G,K,C,I,E,C,E,G,I,C,C,S,G,E,C,E,G,C,C,Q,I,C,A,C,I,C,C,S,A,K,E,I,C,I,C,I,E,I,A,K,C,A,C,I,E,I,C,G,E,C,E,C,E,I,E,O;Y,I,I,G,G,K;gB,E,C,E,G;gB,E,K;a;Y,O,E,C,E;gB,K;gB,K;oB,I;oB;gB,K;oB,E,K;oB,O;wB,O,E,C,E;wB,M;oB;gB,K;oB,E,K;oB,I,E,C,E;oB,K;wB;qB;oB;gB,K;oB,K,E,G,C,G;oB,E,I,C,G;oB;gB;oB,I,C,C,I,E,I,E,I,E,M,G,K,C,C,E,M,G,E,A,K,C,E,C,E,K,K,E,C,E,K,C,G;wB,I;wB;oB;oB,I,E,C,E,K,K,C,C,K,E,C,E,G,C,C,E,I,E,C,E,G,C,C,E,G;wB,E,K,G,E,C,E;wB;oB;oB,I,E,C,E,K,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,I;wB;oB;oB,I,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,E,G,C,I,C;wB;oB;oB,I,C,C,E,E,E,G,C,G;oB,E,I,C,G;oB;Y;Y,K,K,I,C,S;Q,E,O,G;Y,K;gB;gB;a;Y,I;Q,S;Y,I,I;Q;Q,I,E,C,E,G,G,M,E,C,E;Q,O;Y,O,E,C,E,G,E,C,E,G,K;Y,M;Q;I;A;AAwEA,IAA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","sources":["src/index.ts"],"sourcesContent":["import { autoUpdate, computePosition } from \"@floating-ui/dom\";\n\ntype Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"right\"\n | \"right-start\"\n | \"right-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"left-start\"\n | \"left-end\";\n\ninterface MarkOptions {\n /**\n * A CSS selector to specify the elements to be marked.\n */\n selector?: string;\n /**\n * A CSS style to apply to the label element.\n * You can also specify a function that returns a CSS style object.\n */\n markStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * The placement of the mark relative to the element.\n *\n * @default 'top-start'\n */\n markPlacement?: Placement;\n /**\n * A CSS style to apply to the bounding box element.\n * You can also specify a function that returns a CSS style object.\n * Bounding boxes are only shown if `showMasks` is `true`.\n */\n maskStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * Whether or not to show bounding boxes around the elements.\n *\n * @default true\n */\n showMasks?: boolean;\n /**\n * Provide a function for generating labels.\n * By default, labels are generated as numbers starting from 0.\n */\n labelGenerator?: (element: Element, index: number) => string;\n /**\n * Provide a container element to query the elements to be marked.\n * By default, the container element is `document.body`.\n */\n containerElement?: Element;\n /**\n * Only mark elements that are visible in the current viewport\n *\n * @default false\n */\n viewPortOnly?: boolean;\n}\n\ninterface MarkedElement {\n element: Element;\n markElement: HTMLElement;\n maskElement?: HTMLElement;\n}\n\nlet cleanupFns: (() => void)[] = [];\n\nasync function mark(\n options: MarkOptions = {}\n): Promise<Map<string, MarkedElement>> {\n const {\n selector = \"button, input, a, select, textarea\",\n markStyle = {\n backgroundColor: \"red\",\n color: \"white\",\n padding: \"2px 4px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n },\n markPlacement = \"top-start\",\n maskStyle = {\n outline: \"2px dashed red\",\n backgroundColor: \"transparent\",\n },\n showMasks = true,\n labelGenerator = (_, index) => index.toString(),\n containerElement = document.body,\n viewPortOnly = false,\n } = options;\n\n const elements = Array.from(\n containerElement.querySelectorAll(selector)\n ).filter(\n (el) => !viewPortOnly || el.getBoundingClientRect().top < window.innerHeight\n );\n\n const markedElements = new Map<string, MarkedElement>();\n\n await Promise.all(\n elements.map(async (element, index) => {\n const label = labelGenerator(element, index);\n const markElement = createMark(element, markStyle, label, markPlacement);\n\n const maskElement = showMasks\n ? createMask(element, maskStyle, label)\n : undefined;\n\n markedElements.set(label, { element, markElement, maskElement });\n element.setAttribute(\"data-webmarkeredby\", `webmarker-${label}`);\n })\n );\n\n document.documentElement.dataset.webmarkered = \"true\";\n return markedElements;\n}\n\nfunction createMark(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string,\n markPlacement: Placement = \"top-start\"\n): HTMLElement {\n const markElement = document.createElement(\"div\");\n markElement.className = \"webmarker\";\n markElement.id = `webmarker-${label}`;\n markElement.textContent = label;\n document.body.appendChild(markElement);\n positionMark(markElement, element, markPlacement);\n applyStyle(\n markElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return markElement;\n}\n\nfunction createMask(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string\n): HTMLElement {\n const maskElement = document.createElement(\"div\");\n maskElement.className = \"webmarkermask\";\n maskElement.id = `webmarkermask-${label}`;\n document.body.appendChild(maskElement);\n positionMask(maskElement, element);\n applyStyle(\n maskElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return maskElement;\n}\n\nfunction positionMark(\n markElement: HTMLElement,\n element: Element,\n markPlacement: Placement\n) {\n async function updatePosition() {\n const { x, y } = await computePosition(element, markElement, {\n placement: markPlacement,\n });\n Object.assign(markElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n cleanupFns.push(autoUpdate(element, markElement, updatePosition));\n}\n\nasync function positionMask(mask: HTMLElement, element: Element) {\n const { width, height } = element.getBoundingClientRect();\n async function updatePosition() {\n const { x: maskX, y: maskY } = await computePosition(element, mask, {\n placement: \"top-start\",\n });\n Object.assign(mask.style, {\n left: `${maskX}px`,\n top: `${maskY + height}px`,\n width: `${width}px`,\n height: `${height}px`,\n });\n }\n cleanupFns.push(autoUpdate(element, mask, updatePosition));\n}\n\nfunction applyStyle(\n element: HTMLElement,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>,\n customStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n Object.assign(element.style, defaultStyle, customStyle);\n}\n\nfunction unmark(): void {\n document\n .querySelectorAll(\".webmarker, .webmarkermask\")\n .forEach((el) => el.remove());\n document.documentElement.removeAttribute(\"data-webmarkered\");\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n}\n\nfunction isMarked(): boolean {\n return document.documentElement.hasAttribute(\"data-webmarkered\");\n}\n\nwindow[\"mark\"] = mark;\nwindow[\"unmark\"] = unmark;\nwindow[\"isMarked\"] = isMarked;\n\nexport { mark, unmark, isMarked };\nexport type { MarkOptions, MarkedElement, Placement };\n"],"names":[],"version":3,"file":"main.js.map"}
package/dist/module.js CHANGED
@@ -292,6 +292,9 @@ function $2df9d79b6788b58e$export$3522532a010241a0() {
292
292
  function $2df9d79b6788b58e$export$3b489baf08eae67a() {
293
293
  return document.documentElement.hasAttribute("data-webmarkered");
294
294
  }
295
+ window["mark"] = $2df9d79b6788b58e$export$bf7f2fce5c1cf636;
296
+ window["unmark"] = $2df9d79b6788b58e$export$3522532a010241a0;
297
+ window["isMarked"] = $2df9d79b6788b58e$export$3b489baf08eae67a;
295
298
 
296
299
 
297
300
  export {$2df9d79b6788b58e$export$bf7f2fce5c1cf636 as mark, $2df9d79b6788b58e$export$3522532a010241a0 as unmark, $2df9d79b6788b58e$export$3b489baf08eae67a as isMarked};
@@ -1 +1 @@
1
- {"mappings":";;;A,I,kC,a,U,S,I,S,O,E,U,E,C,E,S;I,S,M,K;Q,O,iB,I,Q,I,E,S,O;Y,Q;Q;I;I,O,I,C,K,C,I,O,C,E,S,O,E,M;Q,S,U,K;Y,I;gB,K,U,I,C;Y,E,O,G;gB,O;Y;Q;Q,S,S,K;Y,I;gB,K,S,C,Q,C;Y,E,O,G;gB,O;Y;Q;Q,S,K,M;Y,O,I,G,Q,O,K,I,M,O,K,E,I,C,W;Q;Q,K,A,C,Y,U,K,C,S,c,E,C,E,I;I;A;A,I,oC,a,U,W,I,S,O,E,I;I,I,I;Q,O;Q,M;Y,I,C,C,E,G,G,M,C,C,E;Y,O,C,C,E;Q;Q,M,E;Q,K,E;I,G,G,G,G;I,O,I;Q,M,K;Q,S,K;Q,U,K;I,G,O,W,c,C,C,C,O,Q,C,G;Q,O,I;I,C,G;I,S,K,C;Q,O,S,C;Y,O,K;gB;gB;a;Q;I;I,S,K,E;Q,I,G,M,I,U;Q,M,K,C,I,G,E,C,E,I,C,I,C,C,G,E,I;Y,I,I,G,K,C,I,E,C,E,G,I,C,C,S,G,E,C,E,G,C,C,Q,I,C,A,C,I,C,C,S,A,K,E,I,C,I,C,I,E,I,A,K,C,A,C,I,E,I,C,G,E,C,E,C,E,I,E,O;Y,I,I,G,G,K;gB,E,C,E,G;gB,E,K;a;Y,O,E,C,E;gB,K;gB,K;oB,I;oB;gB,K;oB,E,K;oB,O;wB,O,E,C,E;wB,M;oB;gB,K;oB,E,K;oB,I,E,C,E;oB,K;wB;qB;oB;gB,K;oB,K,E,G,C,G;oB,E,I,C,G;oB;gB;oB,I,C,C,I,E,I,E,I,E,M,G,K,C,C,E,M,G,E,A,K,C,E,C,E,K,K,E,C,E,K,C,G;wB,I;wB;oB;oB,I,E,C,E,K,K,C,C,K,E,C,E,G,C,C,E,I,E,C,E,G,C,C,E,G;wB,E,K,G,E,C,E;wB;oB;oB,I,E,C,E,K,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,I;wB;oB;oB,I,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,E,G,C,I,C;wB;oB;oB,I,C,C,E,E,E,G,C,G;oB,E,I,C,G;oB;Y;Y,K,K,I,C,S;Q,E,O,G;Y,K;gB;gB;a;Y,I;Q,S;Y,I,I;Q;Q,I,E,C,E,G,G,M,E,C,E;Q,O;Y,O,E,C,E,G,E,C,E,G,K;Y,M;Q;I;A;AAwEA,IAAI,mCAA6B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","sources":["src/index.ts"],"sourcesContent":["import { autoUpdate, computePosition } from \"@floating-ui/dom\";\n\ntype Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"right\"\n | \"right-start\"\n | \"right-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"left-start\"\n | \"left-end\";\n\ninterface MarkOptions {\n /**\n * A CSS selector to specify the elements to be marked.\n */\n selector?: string;\n /**\n * A CSS style to apply to the label element.\n * You can also specify a function that returns a CSS style object.\n */\n markStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * The placement of the mark relative to the element.\n *\n * @default 'top-start'\n */\n markPlacement?: Placement;\n /**\n * A CSS style to apply to the bounding box element.\n * You can also specify a function that returns a CSS style object.\n * Bounding boxes are only shown if `showMasks` is `true`.\n */\n maskStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * Whether or not to show bounding boxes around the elements.\n *\n * @default true\n */\n showMasks?: boolean;\n /**\n * Provide a function for generating labels.\n * By default, labels are generated as numbers starting from 0.\n */\n labelGenerator?: (element: Element, index: number) => string;\n /**\n * Provide a container element to query the elements to be marked.\n * By default, the container element is `document.body`.\n */\n containerElement?: Element;\n /**\n * Only mark elements that are visible in the current viewport\n *\n * @default false\n */\n viewPortOnly?: boolean;\n}\n\ninterface MarkedElement {\n element: Element;\n markElement: HTMLElement;\n maskElement?: HTMLElement;\n}\n\nlet cleanupFns: (() => void)[] = [];\n\nasync function mark(\n options: MarkOptions = {}\n): Promise<Map<string, MarkedElement>> {\n const {\n selector = \"button, input, a, select, textarea\",\n markStyle = {\n backgroundColor: \"red\",\n color: \"white\",\n padding: \"2px 4px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n },\n markPlacement = \"top-start\",\n maskStyle = {\n outline: \"2px dashed red\",\n backgroundColor: \"transparent\",\n },\n showMasks = true,\n labelGenerator = (_, index) => index.toString(),\n containerElement = document.body,\n viewPortOnly = false,\n } = options;\n\n const elements = Array.from(\n containerElement.querySelectorAll(selector)\n ).filter(\n (el) => !viewPortOnly || el.getBoundingClientRect().top < window.innerHeight\n );\n\n const markedElements = new Map<string, MarkedElement>();\n\n await Promise.all(\n elements.map(async (element, index) => {\n const label = labelGenerator(element, index);\n const markElement = createMark(element, markStyle, label, markPlacement);\n\n const maskElement = showMasks\n ? createMask(element, maskStyle, label)\n : undefined;\n\n markedElements.set(label, { element, markElement, maskElement });\n element.setAttribute(\"data-webmarkeredby\", `webmarker-${label}`);\n })\n );\n\n document.documentElement.dataset.webmarkered = \"true\";\n return markedElements;\n}\n\nfunction createMark(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string,\n markPlacement: Placement = \"top-start\"\n): HTMLElement {\n const markElement = document.createElement(\"div\");\n markElement.className = \"webmarker\";\n markElement.id = `webmarker-${label}`;\n markElement.textContent = label;\n document.body.appendChild(markElement);\n positionMark(markElement, element, markPlacement);\n applyStyle(\n markElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return markElement;\n}\n\nfunction createMask(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string\n): HTMLElement {\n const maskElement = document.createElement(\"div\");\n maskElement.className = \"webmarkermask\";\n maskElement.id = `webmarkermask-${label}`;\n document.body.appendChild(maskElement);\n positionMask(maskElement, element);\n applyStyle(\n maskElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return maskElement;\n}\n\nfunction positionMark(\n markElement: HTMLElement,\n element: Element,\n markPlacement: Placement\n) {\n async function updatePosition() {\n const { x, y } = await computePosition(element, markElement, {\n placement: markPlacement,\n });\n Object.assign(markElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n cleanupFns.push(autoUpdate(element, markElement, updatePosition));\n}\n\nasync function positionMask(mask: HTMLElement, element: Element) {\n const { width, height } = element.getBoundingClientRect();\n async function updatePosition() {\n const { x: maskX, y: maskY } = await computePosition(element, mask, {\n placement: \"top-start\",\n });\n Object.assign(mask.style, {\n left: `${maskX}px`,\n top: `${maskY + height}px`,\n width: `${width}px`,\n height: `${height}px`,\n });\n }\n cleanupFns.push(autoUpdate(element, mask, updatePosition));\n}\n\nfunction applyStyle(\n element: HTMLElement,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>,\n customStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n Object.assign(element.style, defaultStyle, customStyle);\n}\n\nfunction unmark(): void {\n document\n .querySelectorAll(\".webmarker, .webmarkermask\")\n .forEach((el) => el.remove());\n document.documentElement.removeAttribute(\"data-webmarkered\");\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n}\n\nfunction isMarked(): boolean {\n return document.documentElement.hasAttribute(\"data-webmarkered\");\n}\n\nexport { mark, unmark, isMarked };\nexport type { MarkOptions, MarkedElement, Placement };\n"],"names":[],"version":3,"file":"module.js.map"}
1
+ {"mappings":";;;A,I,kC,a,U,S,I,S,O,E,U,E,C,E,S;I,S,M,K;Q,O,iB,I,Q,I,E,S,O;Y,Q;Q;I;I,O,I,C,K,C,I,O,C,E,S,O,E,M;Q,S,U,K;Y,I;gB,K,U,I,C;Y,E,O,G;gB,O;Y;Q;Q,S,S,K;Y,I;gB,K,S,C,Q,C;Y,E,O,G;gB,O;Y;Q;Q,S,K,M;Y,O,I,G,Q,O,K,I,M,O,K,E,I,C,W;Q;Q,K,A,C,Y,U,K,C,S,c,E,C,E,I;I;A;A,I,oC,a,U,W,I,S,O,E,I;I,I,I;Q,O;Q,M;Y,I,C,C,E,G,G,M,C,C,E;Y,O,C,C,E;Q;Q,M,E;Q,K,E;I,G,G,G,G;I,O,I;Q,M,K;Q,S,K;Q,U,K;I,G,O,W,c,C,C,C,O,Q,C,G;Q,O,I;I,C,G;I,S,K,C;Q,O,S,C;Y,O,K;gB;gB;a;Q;I;I,S,K,E;Q,I,G,M,I,U;Q,M,K,C,I,G,E,C,E,I,C,I,C,C,G,E,I;Y,I,I,G,K,C,I,E,C,E,G,I,C,C,S,G,E,C,E,G,C,C,Q,I,C,A,C,I,C,C,S,A,K,E,I,C,I,C,I,E,I,A,K,C,A,C,I,E,I,C,G,E,C,E,C,E,I,E,O;Y,I,I,G,G,K;gB,E,C,E,G;gB,E,K;a;Y,O,E,C,E;gB,K;gB,K;oB,I;oB;gB,K;oB,E,K;oB,O;wB,O,E,C,E;wB,M;oB;gB,K;oB,E,K;oB,I,E,C,E;oB,K;wB;qB;oB;gB,K;oB,K,E,G,C,G;oB,E,I,C,G;oB;gB;oB,I,C,C,I,E,I,E,I,E,M,G,K,C,C,E,M,G,E,A,K,C,E,C,E,K,K,E,C,E,K,C,G;wB,I;wB;oB;oB,I,E,C,E,K,K,C,C,K,E,C,E,G,C,C,E,I,E,C,E,G,C,C,E,G;wB,E,K,G,E,C,E;wB;oB;oB,I,E,C,E,K,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,I;wB;oB;oB,I,K,E,K,G,C,C,E,E;wB,E,K,G,C,C,E;wB,E,G,C,I,C;wB;oB;oB,I,C,C,E,E,E,G,C,G;oB,E,I,C,G;oB;Y;Y,K,K,I,C,S;Q,E,O,G;Y,K;gB;gB;a;Y,I;Q,S;Y,I,I;Q;Q,I,E,C,E,G,G,M,E,C,E;Q,O;Y,O,E,C,E,G,E,C,E,G,K;Y,M;Q;I;A;AAwEA,IAAI,mCAA6B,EAAE;AAEnC,SAAe;IAEZ,OAAA,gCAAA,IAAA,EAAA,WAAA,SAAO,SADR,OAAyB;Q,I,I,U,I,W,I,e,I,W,I,W,I,gB,I,kB,I,c,U;Q,I,Q,I;QAAzB,IAAA,YAAA,KAAA,GAAA,UAAA,CAAA;Q,O,kC,I,E,S,E;Y,O,G,K;gB,K;oBAGE,KAiBE,QAAO,QAjBsC,EAA/C,WAAQ,OAAA,KAAA,IAAG,uCAAoC,IAC/C,KAgBE,QAAO,SAVR,EAND,YAAS,OAAA,KAAA,IAAG;wBACV,iBAAiB;wBACjB,OAAO;wBACP,SAAS;wBACT,UAAU;wBACV,YAAY;oBACb,IAAA,IACD,KASE,QAAO,aATkB,EAA3B,gBAAa,OAAA,KAAA,IAAG,cAAW,IAC3B,KAQE,QAAO,SALR,EAHD,YAAS,OAAA,KAAA,IAAG;wBACV,SAAS;wBACT,iBAAiB;oBAClB,IAAA,IACD,KAIE,QAAO,SAJO,EAAhB,YAAS,OAAA,KAAA,IAAG,OAAI,IAChB,KAGE,QAAO,cAHsC,EAA/C,iBAAc,OAAA,KAAA,IAAG,SAAC,CAAC,EAAE,KAAK;wBAAK,OAAA,MAAM,QAAQ;oBAAd,IAAgB,IAC/C,KAEE,QAAO,gBAFuB,EAAhC,mBAAgB,OAAA,KAAA,IAAG,SAAS,IAAI,GAAA,IAChC,KACE,QAAO,YADW,EAApB,eAAY,OAAA,KAAA,IAAG,QAAK;oBAGhB,WAAW,MAAM,IAAI,CACzB,iBAAiB,gBAAgB,CAAC,WAClC,MAAM,CACN,SAAC,EAAE;wBAAK,OAAA,CAAC,gBAAgB,GAAG,qBAAqB,GAAG,GAAG,GAAG,OAAO,WAAW;oBAApE;oBAGJ,iBAAiB,IAAI;oBAE3B,OAAA;wBAAA,EAAA,OAAA;wBAAM,QAAQ,GAAG,CACf,SAAS,GAAG,CAAC,SAAO,OAAO,EAAE,KAAK;4BADpC,OAAA,gCAAA,OAAA,KAAA,GAAA,KAAA,GAAA;gC,I,O,a;gC,O,kC,I,E,S,E;oCAEU,QAAQ,eAAe,SAAS;oCAChC,cAAc,iCAAW,SAAS,WAAW,OAAO;oCAEpD,cAAc,YAChB,iCAAW,SAAS,WAAW,SAC/B;oCAEJ,eAAe,GAAG,CAAC,OAAO;wCAAE,SAAO;wCAAE,aAAW;wCAAE,aAAW;oCAAA;oCAC7D,QAAQ,YAAY,CAAC,sBAAsB,aAAA,MAAA,CAAa;oC,O;wC,E,Q;qC;gC;4BACzD;wBAAA;qBACF;gB,K;oBAZD,GAAA,IAAA;oBAcA,SAAS,eAAe,CAAC,OAAO,CAAC,WAAW,GAAG;oBAC/C,OAAA;wBAAA,EAAA,QAAA;wBAAO;qBAAc;Y;Q;I;AACtB;AAED,SAAS,iCACP,OAAgB,EAChB,KAEkE,EAClE,KAAa,EACb,aAAsC;IAAtC,IAAA,kBAAA,KAAA,GAAA,gBAAA;IAEA,IAAM,cAAc,SAAS,aAAa,CAAC;IAC3C,YAAY,SAAS,GAAG;IACxB,YAAY,EAAE,GAAG,aAAA,MAAA,CAAa;IAC9B,YAAY,WAAW,GAAG;IAC1B,SAAS,IAAI,CAAC,WAAW,CAAC;IAC1B,mCAAa,aAAa,SAAS;IACnC,iCACE,aACA;QACE,QAAQ;QACR,UAAU;QACV,eAAe;IAChB,GACD,OAAO,UAAU,aAAa,MAAM,WAAW;IAEjD,OAAO;AACT;AAEA,SAAS,iCACP,OAAgB,EAChB,KAEkE,EAClE,KAAa;IAEb,IAAM,cAAc,SAAS,aAAa,CAAC;IAC3C,YAAY,SAAS,GAAG;IACxB,YAAY,EAAE,GAAG,iBAAA,MAAA,CAAiB;IAClC,SAAS,IAAI,CAAC,WAAW,CAAC;IAC1B,mCAAa,aAAa;IAC1B,iCACE,aACA;QACE,QAAQ;QACR,UAAU;QACV,eAAe;IAChB,GACD,OAAO,UAAU,aAAa,MAAM,WAAW;IAEjD,OAAO;AACT;AAEA,SAAS,mCACP,WAAwB,EACxB,OAAgB,EAChB,aAAwB;IAExB,SAAe;Q,O,gC,I,E,K,G,K,G;Y,I,I,G;Y,O,kC,I,E,S,E;gB,O,G,K;oBACI,KAAA;wBAAA,OAAA;4BAAA,EAAA,OAAA;4BAAM,CAAA,GAAA,sBAAA,EAAgB,SAAS,aAAa;gCAC3D,WAAW;4BACZ;yBAAC;oB,K;wBAFI,KAAW,GAAA,IAAA,IAAT,IAAC,GAAA,CAAA,EAAE,IAAC,GAAA,CAAA;wBAGZ,OAAO,MAAM,CAAC,YAAY,KAAK,EAAE;4BAC/B,MAAM,GAAA,MAAA,CAAG,GAAC;4BACV,KAAK,GAAA,MAAA,CAAG,GAAC;wBACV;wB,O;4B,E,Q;yB;gB;Y;Q;IACF;IAED,iCAAW,IAAI,CAAC,CAAA,GAAA,iBAAA,EAAW,SAAS,aAAa;AACnD;AAEA,SAAe,mCAAa,IAAiB,EAAE,OAAgB;I,O,gC,I,E,K,G,K,G;QAE7D,SAAe;Y,O,gC,I,E,K,G,K,G;gB,I,I,O;gB,O,kC,I,E,S,E;oB,O,G,K;wBACkB,KAAA;4BAAA,OAAA;gCAAA,EAAA,OAAA;gCAAM,CAAA,GAAA,sBAAA,EAAgB,SAAS,MAAM;oCAClE,WAAW;gCACZ;6BAAC;wB,K;4BAFI,KAAyB,GAAA,IAAA,IAApB,QAAK,GAAA,CAAA,EAAK,QAAK,GAAA,CAAA;4BAG1B,OAAO,MAAM,CAAC,KAAK,KAAK,EAAE;gCACxB,MAAM,GAAA,MAAA,CAAG,OAAK;gCACd,KAAK,GAAA,MAAA,CAAG,QAAQ,QAAM;gCACtB,OAAO,GAAA,MAAA,CAAG,OAAK;gCACf,QAAQ,GAAA,MAAA,CAAG,QAAM;4BAClB;4B,O;gC,E,Q;6B;oB;gB;Y;QACF;Q,I,I,O;Q,O,kC,I,E,S,E;YAXK,KAAoB,QAAQ,qBAAqB,IAA/C,QAAK,GAAA,KAAA,EAAE,SAAM,GAAA,MAAA;YAYrB,iCAAW,IAAI,CAAC,CAAA,GAAA,iBAAA,EAAW,SAAS,MAAM;Y,O;gB,E,Q;a;Q;I;AAC3C;AAED,SAAS,iCACP,OAAoB,EACpB,YAAoD,EACpD,WAAmD;IAEnD,OAAO,MAAM,CAAC,QAAQ,KAAK,EAAE,cAAc;AAC7C;AAEA,SAAS;IACP,SACG,gBAAgB,CAAC,8BACjB,OAAO,CAAC,SAAC,EAAE;QAAK,OAAA,GAAG,MAAM;IAAT;IACnB,SAAS,eAAe,CAAC,eAAe,CAAC;IACzC,iCAAW,OAAO,CAAC,SAAC,EAAE;QAAK,OAAA;IAAA;IAC3B,mCAAa,EAAE;AACjB;AAEA,SAAS;IACP,OAAO,SAAS,eAAe,CAAC,YAAY,CAAC;AAC/C;AAEA,MAAM,CAAC,OAAO,GAAG;AACjB,MAAM,CAAC,SAAS,GAAG;AACnB,MAAM,CAAC,WAAW,GAAG","sources":["src/index.ts"],"sourcesContent":["import { autoUpdate, computePosition } from \"@floating-ui/dom\";\n\ntype Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"right\"\n | \"right-start\"\n | \"right-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"left-start\"\n | \"left-end\";\n\ninterface MarkOptions {\n /**\n * A CSS selector to specify the elements to be marked.\n */\n selector?: string;\n /**\n * A CSS style to apply to the label element.\n * You can also specify a function that returns a CSS style object.\n */\n markStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * The placement of the mark relative to the element.\n *\n * @default 'top-start'\n */\n markPlacement?: Placement;\n /**\n * A CSS style to apply to the bounding box element.\n * You can also specify a function that returns a CSS style object.\n * Bounding boxes are only shown if `showMasks` is `true`.\n */\n maskStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * Whether or not to show bounding boxes around the elements.\n *\n * @default true\n */\n showMasks?: boolean;\n /**\n * Provide a function for generating labels.\n * By default, labels are generated as numbers starting from 0.\n */\n labelGenerator?: (element: Element, index: number) => string;\n /**\n * Provide a container element to query the elements to be marked.\n * By default, the container element is `document.body`.\n */\n containerElement?: Element;\n /**\n * Only mark elements that are visible in the current viewport\n *\n * @default false\n */\n viewPortOnly?: boolean;\n}\n\ninterface MarkedElement {\n element: Element;\n markElement: HTMLElement;\n maskElement?: HTMLElement;\n}\n\nlet cleanupFns: (() => void)[] = [];\n\nasync function mark(\n options: MarkOptions = {}\n): Promise<Map<string, MarkedElement>> {\n const {\n selector = \"button, input, a, select, textarea\",\n markStyle = {\n backgroundColor: \"red\",\n color: \"white\",\n padding: \"2px 4px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n },\n markPlacement = \"top-start\",\n maskStyle = {\n outline: \"2px dashed red\",\n backgroundColor: \"transparent\",\n },\n showMasks = true,\n labelGenerator = (_, index) => index.toString(),\n containerElement = document.body,\n viewPortOnly = false,\n } = options;\n\n const elements = Array.from(\n containerElement.querySelectorAll(selector)\n ).filter(\n (el) => !viewPortOnly || el.getBoundingClientRect().top < window.innerHeight\n );\n\n const markedElements = new Map<string, MarkedElement>();\n\n await Promise.all(\n elements.map(async (element, index) => {\n const label = labelGenerator(element, index);\n const markElement = createMark(element, markStyle, label, markPlacement);\n\n const maskElement = showMasks\n ? createMask(element, maskStyle, label)\n : undefined;\n\n markedElements.set(label, { element, markElement, maskElement });\n element.setAttribute(\"data-webmarkeredby\", `webmarker-${label}`);\n })\n );\n\n document.documentElement.dataset.webmarkered = \"true\";\n return markedElements;\n}\n\nfunction createMark(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string,\n markPlacement: Placement = \"top-start\"\n): HTMLElement {\n const markElement = document.createElement(\"div\");\n markElement.className = \"webmarker\";\n markElement.id = `webmarker-${label}`;\n markElement.textContent = label;\n document.body.appendChild(markElement);\n positionMark(markElement, element, markPlacement);\n applyStyle(\n markElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return markElement;\n}\n\nfunction createMask(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string\n): HTMLElement {\n const maskElement = document.createElement(\"div\");\n maskElement.className = \"webmarkermask\";\n maskElement.id = `webmarkermask-${label}`;\n document.body.appendChild(maskElement);\n positionMask(maskElement, element);\n applyStyle(\n maskElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return maskElement;\n}\n\nfunction positionMark(\n markElement: HTMLElement,\n element: Element,\n markPlacement: Placement\n) {\n async function updatePosition() {\n const { x, y } = await computePosition(element, markElement, {\n placement: markPlacement,\n });\n Object.assign(markElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n cleanupFns.push(autoUpdate(element, markElement, updatePosition));\n}\n\nasync function positionMask(mask: HTMLElement, element: Element) {\n const { width, height } = element.getBoundingClientRect();\n async function updatePosition() {\n const { x: maskX, y: maskY } = await computePosition(element, mask, {\n placement: \"top-start\",\n });\n Object.assign(mask.style, {\n left: `${maskX}px`,\n top: `${maskY + height}px`,\n width: `${width}px`,\n height: `${height}px`,\n });\n }\n cleanupFns.push(autoUpdate(element, mask, updatePosition));\n}\n\nfunction applyStyle(\n element: HTMLElement,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>,\n customStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n Object.assign(element.style, defaultStyle, customStyle);\n}\n\nfunction unmark(): void {\n document\n .querySelectorAll(\".webmarker, .webmarkermask\")\n .forEach((el) => el.remove());\n document.documentElement.removeAttribute(\"data-webmarkered\");\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n}\n\nfunction isMarked(): boolean {\n return document.documentElement.hasAttribute(\"data-webmarkered\");\n}\n\nwindow[\"mark\"] = mark;\nwindow[\"unmark\"] = unmark;\nwindow[\"isMarked\"] = isMarked;\n\nexport { mark, unmark, isMarked };\nexport type { MarkOptions, MarkedElement, Placement };\n"],"names":[],"version":3,"file":"module.js.map"}
@@ -1 +1 @@
1
- {"mappings":"AAEA,wBACI,KAAK,GACL,WAAW,GACX,SAAS,GACT,OAAO,GACP,aAAa,GACb,WAAW,GACX,QAAQ,GACR,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,UAAU,CAAC;AAEf;IACE;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EACN,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,GACtC,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACnE;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EACN,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,GACtC,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACnE;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;IACE,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAID,qBACE,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CA6CrC;AA8FD,0BAAmB,IAAI,CAOtB;AAED,4BAAqB,OAAO,CAE3B","sources":["src/src/index.ts","src/index.ts"],"sourcesContent":[null,"import { autoUpdate, computePosition } from \"@floating-ui/dom\";\n\ntype Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"right\"\n | \"right-start\"\n | \"right-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"left-start\"\n | \"left-end\";\n\ninterface MarkOptions {\n /**\n * A CSS selector to specify the elements to be marked.\n */\n selector?: string;\n /**\n * A CSS style to apply to the label element.\n * You can also specify a function that returns a CSS style object.\n */\n markStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * The placement of the mark relative to the element.\n *\n * @default 'top-start'\n */\n markPlacement?: Placement;\n /**\n * A CSS style to apply to the bounding box element.\n * You can also specify a function that returns a CSS style object.\n * Bounding boxes are only shown if `showMasks` is `true`.\n */\n maskStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * Whether or not to show bounding boxes around the elements.\n *\n * @default true\n */\n showMasks?: boolean;\n /**\n * Provide a function for generating labels.\n * By default, labels are generated as numbers starting from 0.\n */\n labelGenerator?: (element: Element, index: number) => string;\n /**\n * Provide a container element to query the elements to be marked.\n * By default, the container element is `document.body`.\n */\n containerElement?: Element;\n /**\n * Only mark elements that are visible in the current viewport\n *\n * @default false\n */\n viewPortOnly?: boolean;\n}\n\ninterface MarkedElement {\n element: Element;\n markElement: HTMLElement;\n maskElement?: HTMLElement;\n}\n\nlet cleanupFns: (() => void)[] = [];\n\nasync function mark(\n options: MarkOptions = {}\n): Promise<Map<string, MarkedElement>> {\n const {\n selector = \"button, input, a, select, textarea\",\n markStyle = {\n backgroundColor: \"red\",\n color: \"white\",\n padding: \"2px 4px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n },\n markPlacement = \"top-start\",\n maskStyle = {\n outline: \"2px dashed red\",\n backgroundColor: \"transparent\",\n },\n showMasks = true,\n labelGenerator = (_, index) => index.toString(),\n containerElement = document.body,\n viewPortOnly = false,\n } = options;\n\n const elements = Array.from(\n containerElement.querySelectorAll(selector)\n ).filter(\n (el) => !viewPortOnly || el.getBoundingClientRect().top < window.innerHeight\n );\n\n const markedElements = new Map<string, MarkedElement>();\n\n await Promise.all(\n elements.map(async (element, index) => {\n const label = labelGenerator(element, index);\n const markElement = createMark(element, markStyle, label, markPlacement);\n\n const maskElement = showMasks\n ? createMask(element, maskStyle, label)\n : undefined;\n\n markedElements.set(label, { element, markElement, maskElement });\n element.setAttribute(\"data-webmarkeredby\", `webmarker-${label}`);\n })\n );\n\n document.documentElement.dataset.webmarkered = \"true\";\n return markedElements;\n}\n\nfunction createMark(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string,\n markPlacement: Placement = \"top-start\"\n): HTMLElement {\n const markElement = document.createElement(\"div\");\n markElement.className = \"webmarker\";\n markElement.id = `webmarker-${label}`;\n markElement.textContent = label;\n document.body.appendChild(markElement);\n positionMark(markElement, element, markPlacement);\n applyStyle(\n markElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return markElement;\n}\n\nfunction createMask(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string\n): HTMLElement {\n const maskElement = document.createElement(\"div\");\n maskElement.className = \"webmarkermask\";\n maskElement.id = `webmarkermask-${label}`;\n document.body.appendChild(maskElement);\n positionMask(maskElement, element);\n applyStyle(\n maskElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return maskElement;\n}\n\nfunction positionMark(\n markElement: HTMLElement,\n element: Element,\n markPlacement: Placement\n) {\n async function updatePosition() {\n const { x, y } = await computePosition(element, markElement, {\n placement: markPlacement,\n });\n Object.assign(markElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n cleanupFns.push(autoUpdate(element, markElement, updatePosition));\n}\n\nasync function positionMask(mask: HTMLElement, element: Element) {\n const { width, height } = element.getBoundingClientRect();\n async function updatePosition() {\n const { x: maskX, y: maskY } = await computePosition(element, mask, {\n placement: \"top-start\",\n });\n Object.assign(mask.style, {\n left: `${maskX}px`,\n top: `${maskY + height}px`,\n width: `${width}px`,\n height: `${height}px`,\n });\n }\n cleanupFns.push(autoUpdate(element, mask, updatePosition));\n}\n\nfunction applyStyle(\n element: HTMLElement,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>,\n customStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n Object.assign(element.style, defaultStyle, customStyle);\n}\n\nfunction unmark(): void {\n document\n .querySelectorAll(\".webmarker, .webmarkermask\")\n .forEach((el) => el.remove());\n document.documentElement.removeAttribute(\"data-webmarkered\");\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n}\n\nfunction isMarked(): boolean {\n return document.documentElement.hasAttribute(\"data-webmarkered\");\n}\n\nexport { mark, unmark, isMarked };\nexport type { MarkOptions, MarkedElement, Placement };\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":"AAEA,wBACI,KAAK,GACL,WAAW,GACX,SAAS,GACT,OAAO,GACP,aAAa,GACb,WAAW,GACX,QAAQ,GACR,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,UAAU,CAAC;AAEf;IACE;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EACN,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,GACtC,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACnE;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EACN,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,GACtC,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IACnE;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7D;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;IACE,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAID,qBACE,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CA6CrC;AA8FD,0BAAmB,IAAI,CAOtB;AAED,4BAAqB,OAAO,CAE3B","sources":["src/src/index.ts","src/index.ts"],"sourcesContent":[null,"import { autoUpdate, computePosition } from \"@floating-ui/dom\";\n\ntype Placement =\n | \"top\"\n | \"top-start\"\n | \"top-end\"\n | \"right\"\n | \"right-start\"\n | \"right-end\"\n | \"bottom\"\n | \"bottom-start\"\n | \"bottom-end\"\n | \"left\"\n | \"left-start\"\n | \"left-end\";\n\ninterface MarkOptions {\n /**\n * A CSS selector to specify the elements to be marked.\n */\n selector?: string;\n /**\n * A CSS style to apply to the label element.\n * You can also specify a function that returns a CSS style object.\n */\n markStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * The placement of the mark relative to the element.\n *\n * @default 'top-start'\n */\n markPlacement?: Placement;\n /**\n * A CSS style to apply to the bounding box element.\n * You can also specify a function that returns a CSS style object.\n * Bounding boxes are only shown if `showMasks` is `true`.\n */\n maskStyle?:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>);\n /**\n * Whether or not to show bounding boxes around the elements.\n *\n * @default true\n */\n showMasks?: boolean;\n /**\n * Provide a function for generating labels.\n * By default, labels are generated as numbers starting from 0.\n */\n labelGenerator?: (element: Element, index: number) => string;\n /**\n * Provide a container element to query the elements to be marked.\n * By default, the container element is `document.body`.\n */\n containerElement?: Element;\n /**\n * Only mark elements that are visible in the current viewport\n *\n * @default false\n */\n viewPortOnly?: boolean;\n}\n\ninterface MarkedElement {\n element: Element;\n markElement: HTMLElement;\n maskElement?: HTMLElement;\n}\n\nlet cleanupFns: (() => void)[] = [];\n\nasync function mark(\n options: MarkOptions = {}\n): Promise<Map<string, MarkedElement>> {\n const {\n selector = \"button, input, a, select, textarea\",\n markStyle = {\n backgroundColor: \"red\",\n color: \"white\",\n padding: \"2px 4px\",\n fontSize: \"12px\",\n fontWeight: \"bold\",\n },\n markPlacement = \"top-start\",\n maskStyle = {\n outline: \"2px dashed red\",\n backgroundColor: \"transparent\",\n },\n showMasks = true,\n labelGenerator = (_, index) => index.toString(),\n containerElement = document.body,\n viewPortOnly = false,\n } = options;\n\n const elements = Array.from(\n containerElement.querySelectorAll(selector)\n ).filter(\n (el) => !viewPortOnly || el.getBoundingClientRect().top < window.innerHeight\n );\n\n const markedElements = new Map<string, MarkedElement>();\n\n await Promise.all(\n elements.map(async (element, index) => {\n const label = labelGenerator(element, index);\n const markElement = createMark(element, markStyle, label, markPlacement);\n\n const maskElement = showMasks\n ? createMask(element, maskStyle, label)\n : undefined;\n\n markedElements.set(label, { element, markElement, maskElement });\n element.setAttribute(\"data-webmarkeredby\", `webmarker-${label}`);\n })\n );\n\n document.documentElement.dataset.webmarkered = \"true\";\n return markedElements;\n}\n\nfunction createMark(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string,\n markPlacement: Placement = \"top-start\"\n): HTMLElement {\n const markElement = document.createElement(\"div\");\n markElement.className = \"webmarker\";\n markElement.id = `webmarker-${label}`;\n markElement.textContent = label;\n document.body.appendChild(markElement);\n positionMark(markElement, element, markPlacement);\n applyStyle(\n markElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return markElement;\n}\n\nfunction createMask(\n element: Element,\n style:\n | Readonly<Partial<CSSStyleDeclaration>>\n | ((element: Element) => Readonly<Partial<CSSStyleDeclaration>>),\n label: string\n): HTMLElement {\n const maskElement = document.createElement(\"div\");\n maskElement.className = \"webmarkermask\";\n maskElement.id = `webmarkermask-${label}`;\n document.body.appendChild(maskElement);\n positionMask(maskElement, element);\n applyStyle(\n maskElement,\n {\n zIndex: \"999999999\",\n position: \"absolute\",\n pointerEvents: \"none\",\n },\n typeof style === \"function\" ? style(element) : style\n );\n return maskElement;\n}\n\nfunction positionMark(\n markElement: HTMLElement,\n element: Element,\n markPlacement: Placement\n) {\n async function updatePosition() {\n const { x, y } = await computePosition(element, markElement, {\n placement: markPlacement,\n });\n Object.assign(markElement.style, {\n left: `${x}px`,\n top: `${y}px`,\n });\n }\n\n cleanupFns.push(autoUpdate(element, markElement, updatePosition));\n}\n\nasync function positionMask(mask: HTMLElement, element: Element) {\n const { width, height } = element.getBoundingClientRect();\n async function updatePosition() {\n const { x: maskX, y: maskY } = await computePosition(element, mask, {\n placement: \"top-start\",\n });\n Object.assign(mask.style, {\n left: `${maskX}px`,\n top: `${maskY + height}px`,\n width: `${width}px`,\n height: `${height}px`,\n });\n }\n cleanupFns.push(autoUpdate(element, mask, updatePosition));\n}\n\nfunction applyStyle(\n element: HTMLElement,\n defaultStyle: Readonly<Partial<CSSStyleDeclaration>>,\n customStyle: Readonly<Partial<CSSStyleDeclaration>>\n): void {\n Object.assign(element.style, defaultStyle, customStyle);\n}\n\nfunction unmark(): void {\n document\n .querySelectorAll(\".webmarker, .webmarkermask\")\n .forEach((el) => el.remove());\n document.documentElement.removeAttribute(\"data-webmarkered\");\n cleanupFns.forEach((fn) => fn());\n cleanupFns = [];\n}\n\nfunction isMarked(): boolean {\n return document.documentElement.hasAttribute(\"data-webmarkered\");\n}\n\nwindow[\"mark\"] = mark;\nwindow[\"unmark\"] = unmark;\nwindow[\"isMarked\"] = isMarked;\n\nexport { mark, unmark, isMarked };\nexport type { MarkOptions, MarkedElement, Placement };\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webmarker-js",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "A library for marking web pages for Set-of-Mark (SoM) prompting with vision-language models.",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/main.js",
package/src/index.ts CHANGED
@@ -226,5 +226,9 @@ function isMarked(): boolean {
226
226
  return document.documentElement.hasAttribute("data-webmarkered");
227
227
  }
228
228
 
229
+ window["mark"] = mark;
230
+ window["unmark"] = unmark;
231
+ window["isMarked"] = isMarked;
232
+
229
233
  export { mark, unmark, isMarked };
230
234
  export type { MarkOptions, MarkedElement, Placement };
package/.babel.config.js DELETED
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- presets: [
3
- ["@babel/preset-env", { targets: { node: "current" } }],
4
- "@babel/preset-typescript",
5
- ],
6
- };
package/.parcelrc DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "@parcel/config-default",
3
- "transformers": {
4
- "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
5
- },
6
- "validators": {
7
- "*.{ts,tsx}": ["@parcel/validator-typescript"]
8
- }
9
- }
package/demo/App.js DELETED
@@ -1,69 +0,0 @@
1
- import { unmark, mark } from "../src";
2
- import { useEffect, useState, useRef } from "react";
3
-
4
- export function App() {
5
- let [isMarked, setMarked] = useState(false);
6
- let [elementMap, setElementMap] = useState(new Map());
7
- let demoRef = useRef(null);
8
-
9
- useEffect(() => {
10
- if (!isMarked) {
11
- let elements = mark({});
12
- elements.then((res) => setElementMap(res));
13
- setMarked(true);
14
- }
15
- }, []);
16
-
17
- let toggleMark = () => {
18
- if (isMarked) {
19
- unmark();
20
- setMarked(false);
21
- setElementMap(new Map());
22
- } else {
23
- let elements = mark({});
24
- elements.then((res) => setElementMap(res));
25
- setMarked(true);
26
- }
27
- };
28
-
29
- return (
30
- <main>
31
- <h1>WebMarker Demo</h1>
32
- <button onClick={toggleMark}>{isMarked ? "Unmark" : "Mark"}</button>
33
- <div ref={demoRef} style={{ display: "flex", flexDirection: "column" }}>
34
- <h2>Interactive Elements:</h2>
35
- <div>
36
- <label htmlFor="field-1">Input:</label>
37
- <input id="field-1" type="text" />
38
- </div>
39
-
40
- <div>
41
- <label htmlFor="field-2">Input:</label>
42
- <input id="field-2" type="text" />
43
- </div>
44
-
45
- <div>
46
- <label htmlFor="field-3">Input:</label>
47
- <input id="field-3" type="text" />
48
- </div>
49
-
50
- <div>
51
- <button>Button 1</button>
52
- <button>Button 2</button>
53
- </div>
54
- </div>
55
- <div>
56
- <h2>Marked Elements:</h2>
57
- <ul>
58
- {Array.from(elementMap)
59
- .map(([key, value]) => ({ key, value }))
60
- .map((element) => (
61
- <li key={element.key}>
62
- {element.key} : {element.value.element.tagName}
63
- </li>
64
- ))}
65
- </ul>
66
- </div>
67
- </main>
68
- );
69
- }
package/demo/index.html DELETED
@@ -1,11 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <title>WebMarker Demo</title>
6
- </head>
7
- <body>
8
- <div id="app"></div>
9
- <script type="module" src="index.js"></script>
10
- </body>
11
- </html>
package/demo/index.js DELETED
@@ -1,6 +0,0 @@
1
- import { createRoot } from "react-dom/client";
2
- import { App } from "./App";
3
-
4
- const container = document.getElementById("app");
5
- const root = createRoot(container);
6
- root.render(<App />);
@@ -1,44 +0,0 @@
1
- "use client";
2
-
3
- import { unmark, mark } from "../../dist/main";
4
- import { useEffect, useState } from "react";
5
-
6
- export function Demo() {
7
- let [isMarked, setMarked] = useState(false);
8
- let [elementMap, setElementMap] = useState(new Map());
9
-
10
- let toggleMark = () => {
11
- if (isMarked) {
12
- unmark();
13
- setMarked(false);
14
- setElementMap(new Map());
15
- } else {
16
- let elements = mark({});
17
- elements.then((res) => setElementMap(res));
18
- setMarked(true);
19
- }
20
- };
21
-
22
- useEffect(() => {
23
- return () => {
24
- if (isMarked) {
25
- unmark();
26
- }
27
- };
28
- }, []);
29
-
30
- return (
31
- <div>
32
- <div className="content-center text-center p-8">
33
- <button
34
- className="text-white bg-blue-700 w-52 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-700 dark:hover:bg-blue-800 focus:outline-none dark:focus:ring-blue-900"
35
- onClick={toggleMark}
36
- >
37
- {isMarked ? "Unmark this page" : "Mark this page"}
38
- </button>
39
- </div>
40
-
41
- <div className="text-center">{elementMap.size} marked elements</div>
42
- </div>
43
- );
44
- }
@@ -1,15 +0,0 @@
1
- "use client";
2
-
3
- import { useConfig } from "nextra-theme-docs";
4
- import Image from "next/image";
5
-
6
- export function Logo() {
7
- const { themeSwitch } = useConfig();
8
- console.log(themeSwitch);
9
-
10
- return themeSwitch ? (
11
- <img src="/webmarker-dark.png" alt="Nextra" width={120} height={40} />
12
- ) : (
13
- <img src="/webmarker-light.png" alt="Nextra" width={120} height={40} />
14
- );
15
- }
@@ -1,7 +0,0 @@
1
- import type { MDXComponents } from "mdx/types";
2
-
3
- export function useMDXComponents(components: MDXComponents): MDXComponents {
4
- return {
5
- ...components,
6
- };
7
- }
package/docs/global.css DELETED
@@ -1,3 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
@@ -1,5 +0,0 @@
1
- /// <reference types="next" />
2
- /// <reference types="next/image-types/global" />
3
-
4
- // NOTE: This file should not be edited
5
- // see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -1,6 +0,0 @@
1
- const withNextra = require('nextra')({
2
- theme: 'nextra-theme-docs',
3
- themeConfig: './theme.config.tsx',
4
- })
5
-
6
- module.exports = withNextra()
@@ -1,7 +0,0 @@
1
- // These styles apply to every route in the application
2
- import "../global.css";
3
- import type { AppProps } from "next/app";
4
-
5
- export default function App({ Component, pageProps }: AppProps) {
6
- return <Component {...pageProps} />;
7
- }
@@ -1,3 +0,0 @@
1
- {
2
- "index": "Get started"
3
- }
@@ -1,82 +0,0 @@
1
- import { Demo } from "../components/Demo";
2
- import { Callout } from "nextra/components";
3
- import { Tabs } from "nextra/components";
4
- import { Logo } from "../components/Logo";
5
-
6
- # WebMarker
7
-
8
- <p align="center">
9
- <Logo />
10
- </p>
11
-
12
- <br />
13
-
14
- <p align="center">Mark web pages for use with vision-language models.</p>
15
-
16
- <Demo />
17
-
18
- ## Overview
19
-
20
- **WebMarker** adds visual markings with labels to elements on a web page. This can be used for [Set-of-Mark (SoM)](https://github.com/microsoft/SoM) prompting, which improves visual grounding abilities of vision-language models such as GPT-4V, Claude 3, and Google Gemini 1.5.
21
-
22
- ## Installation
23
-
24
- <Tabs items={["yarn", "npm", "pnpm"]}>
25
- <Tabs.Tab>```yarn add webmarker-js ```</Tabs.Tab>
26
- <Tabs.Tab>```npm install webmarker-js ```</Tabs.Tab>
27
- <Tabs.Tab>```pnpm add webmarker-js ```</Tabs.Tab>
28
- </Tabs>
29
-
30
- ## Usage
31
-
32
- The `mark()` function will add markings for all interactive elements on a web page, and return a Map of the marked elements. The returned Map's keys are the mark labels, and the values are an object with the element (`element`), mark element (`markElement`), and mask element (`markElement`).
33
-
34
- ```javascript
35
- import { mark, unmark } from "webmarker-js";
36
-
37
- // Mark interactive elements on the document
38
- let elements = await mark();
39
-
40
- // Reference an element by label
41
- console.log(elements.get("0").element);
42
-
43
- // Remove markings
44
- unmark();
45
- ```
46
-
47
- ### Options
48
-
49
- ```javascript
50
- let elements = mark({
51
- // A custom CSS selector for which elements to mark.
52
- // The default selector will select all interactive elements.
53
- selector: "button, input",
54
- // A custom CSS style to apply to the mark element.
55
- markStyle: { color: "white", backgroundColor: "blue", padding: 5 },
56
- // A custom CSS style to apply to the mask element.
57
- // Use a transparent background to have it act as a bounding box.
58
- maskStyle: { outline: "2px dashed blue", backgroundColor: "transparent" },
59
- // The placement of the mark relative to the element.
60
- markPlacement: "top-start";
61
- // Whether or not to show masks over elements.
62
- // Defaults to true.
63
- showMasks: true,
64
- // A custom function for generating the labels.
65
- // Defaults to '0', '1', '2'... if not provided.
66
- labelGenerator: (element, index) => `Element ${index}`,
67
- // A container element to query the elements to be marked.
68
- // Defaults to the document.
69
- containerElement: document.body,
70
- // Only mark elements that are visible in the current viewport
71
- viewPortOnly: true,
72
- });
73
-
74
- // Cleanup
75
- unmark();
76
- ```
77
-
78
- ## Use with Playwright
79
-
80
- <Callout emoji="🚧" type="info">
81
- Coming soon
82
- </Callout>
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- plugins: {
3
- tailwindcss: {},
4
- autoprefixer: {},
5
- },
6
- }
Binary file
Binary file
@@ -1,11 +0,0 @@
1
- /** @type {import('tailwindcss').Config} */
2
- module.exports = {
3
- content: [
4
- "./pages/**/*.{js,ts,jsx,tsx,mdx}",
5
- "./components/**/*.{js,ts,jsx,tsx,mdx}",
6
- ],
7
- theme: {
8
- extend: {},
9
- },
10
- plugins: [],
11
- };
@@ -1,29 +0,0 @@
1
- import React from "react";
2
- import { DocsThemeConfig } from "nextra-theme-docs";
3
- import { useRouter } from "next/router";
4
-
5
- const config: DocsThemeConfig = {
6
- logo: <span>WebMarker</span>,
7
- useNextSeoProps() {
8
- const { pathname } = useRouter();
9
- if (pathname !== "/") {
10
- return {
11
- titleTemplate: "%s – WebMarker",
12
- };
13
- } else {
14
- return {
15
- title: "WebMarker",
16
- description: "Mark web pages for use with vision-language models.",
17
- };
18
- }
19
- },
20
- project: {
21
- link: "https://github.com/reidbarber/webmarker",
22
- },
23
- docsRepositoryBase: "https://github.com/reidbarber/webmarker",
24
- footer: {
25
- text: "Webmarker",
26
- },
27
- };
28
-
29
- export default config;
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es5",
4
- "lib": ["dom", "dom.iterable", "esnext"],
5
- "allowJs": true,
6
- "skipLibCheck": true,
7
- "strict": false,
8
- "forceConsistentCasingInFileNames": true,
9
- "noEmit": true,
10
- "incremental": true,
11
- "esModuleInterop": true,
12
- "module": "esnext",
13
- "moduleResolution": "node",
14
- "resolveJsonModule": true,
15
- "isolatedModules": true,
16
- "jsx": "preserve"
17
- },
18
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "../../src/**/*.ts"],
19
- "exclude": ["node_modules"]
20
- }
package/jest.config.js DELETED
@@ -1,5 +0,0 @@
1
- /** @type {import('ts-jest').JestConfigWithTsJest} */
2
- module.exports = {
3
- preset: "ts-jest",
4
- testEnvironment: "jsdom",
5
- };
@@ -1,59 +0,0 @@
1
- import { mark, unmark, isMarked, MarkOptions } from "../src/index";
2
- import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
3
-
4
- describe("WebMarker", () => {
5
- beforeEach(() => {
6
- document.body.innerHTML = `
7
- <button>Button 1</button>
8
- <button>Button 2</button>
9
- <input type="text" placeholder="Input field" />
10
- <a href="#">Link</a>
11
- `;
12
- });
13
-
14
- afterEach(() => {
15
- unmark();
16
- });
17
-
18
- test("marks all interactive elements", async () => {
19
- const elements = await mark();
20
- expect(elements.size).toBe(4);
21
- expect(isMarked()).toBe(true);
22
- });
23
-
24
- test("assigns correct labels to elements", async () => {
25
- const options: MarkOptions = {
26
- labelGenerator: (_, index) => `Label ${index}`,
27
- };
28
- const elements = await mark(options);
29
-
30
- expect(elements.get("Label 0")?.element.tagName).toBe("BUTTON");
31
- expect(elements.get("Label 1")?.element.tagName).toBe("BUTTON");
32
- expect(elements.get("Label 2")?.element.tagName).toBe("INPUT");
33
- expect(elements.get("Label 3")?.element.tagName).toBe("A");
34
- });
35
-
36
- test("removes marks with unmark()", async () => {
37
- await mark();
38
- expect(isMarked()).toBe(true);
39
- unmark();
40
- expect(document.querySelector(".webmarker")).toBeNull();
41
- expect(document.querySelector(".webmarkermask")).toBeNull();
42
- expect(isMarked()).toBe(false);
43
- });
44
-
45
- test("applies custom styles to marks and masks", async () => {
46
- await mark({
47
- markStyle: { backgroundColor: "blue", color: "red" },
48
- maskStyle: { outline: "3px solid green" },
49
- });
50
-
51
- const markElement = document.querySelector(".webmarker") as HTMLElement;
52
- const maskElement = document.querySelector(".webmarkermask") as HTMLElement;
53
-
54
- expect(markElement.style.backgroundColor).toBe("blue");
55
- expect(markElement.textContent).toBe("0");
56
- expect(markElement.style.color).toBe("red");
57
- expect(maskElement.style.outline).toBe("3px solid green");
58
- });
59
- });
package/tsconfig.json DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "baseUrl": "./src",
4
- "lib": ["dom", "es2015"]
5
- },
6
- "include": ["src/**/*"]
7
- }