webmarker-js 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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,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","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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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":"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
- }