{"_id":"@appliftlabs/poke","_rev":"7-bd785e571e3bcb683fa5d94b1c7110f1","name":"@appliftlabs/poke","dist-tags":{"latest":"0.4.2"},"versions":{"0.1.0":{"name":"@appliftlabs/poke","version":"0.1.0","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"author":{"name":"Applift Labs"},"license":"MIT","_id":"@appliftlabs/poke@0.1.0","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"homepage":"https://github.com/appliftlabs/poke#readme","bugs":{"url":"https://github.com/appliftlabs/poke/issues"},"dist":{"shasum":"5fca432073f28ebf0d66f67d7689546911c979f3","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.1.0.tgz","fileCount":8,"integrity":"sha512-2XsM6QaKQY0bJTcnpmVO8yqPdCeWtrlRQ2mcEcH17puWN77+rEivdiLN0UP7P11n5oqokQ76ONGkuK0LMCQgxA==","signatures":[{"sig":"MEUCIFDtDfF5SwWXjcbpH5sfRQrxXO+RpQ9X0j6Le3bMutPtAiEAlidvo4SnSc6nvmvjcMhCYc90bVppaMDG95XnGFWa5Ks=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":238487},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./global":"./dist/poke.global.js","./package.json":"./package.json"},"gitHead":"dce75b674ee74c0bef0cf764a79acc997c56d321","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"jsdelivr":"./dist/poke.global.js","repository":{"url":"git+https://github.com/appliftlabs/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/poke_0.1.0_1788911262780_0.31790523677545224","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@appliftlabs/poke","version":"0.1.1","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"author":{"name":"Applift Labs"},"license":"MIT","_id":"@appliftlabs/poke@0.1.1","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"homepage":"https://github.com/yusuf-ishaku/poke#readme","bugs":{"url":"https://github.com/yusuf-ishaku/poke/issues"},"dist":{"shasum":"70136eebbbb982a1504c15bc843cb4e07bafd545","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.1.1.tgz","fileCount":8,"integrity":"sha512-N9dGxM9Q0r9PciWMGG+EQbQliOtXdb9ua1dtSmy04uQeDBNrx9956nYXaSRcJSbgJRnH9mzcvQCEwBFbcInwDw==","signatures":[{"sig":"MEUCIDQqO+AtJFHYwTDzI+PjHA0nj1XBSytYQOeTeNyxBMv5AiEA/36UhrJiewwoezTrAiJpRwvkoObVoMHyEKj1AAm7KBw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQC6gp+ZZ1Qz7RIOSyi16yAKOWUl71e6VJRPQ8MWRd1rLgIhAIClTO97mwqSo5zEy5ct3ilK/Qns7SETJVDtLTPW1QfY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243490},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./global":"./dist/poke.global.js","./package.json":"./package.json"},"gitHead":"2a1f32318f55652b8a0bbcc80618892673cf1e92","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"jsdelivr":"./dist/poke.global.js","repository":{"url":"git+https://github.com/yusuf-ishaku/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/poke_0.1.1_1788914052417_0.21406927773688045","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@appliftlabs/poke","version":"0.2.0","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"author":{"name":"Applift Labs"},"license":"MIT","_id":"@appliftlabs/poke@0.2.0","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"homepage":"https://github.com/yusuf-ishaku/poke#readme","bugs":{"url":"https://github.com/yusuf-ishaku/poke/issues"},"dist":{"shasum":"87d72b69359eb72c36ef3b3432c08b3a025aece5","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.2.0.tgz","fileCount":8,"integrity":"sha512-3rxVhdwjArCgIt4bpUQqKlwpCRBZwik0dCQ4uQt4n9l6F1y+ISmnj/lm0VoEI4Zpa4IZ41Ifkh+hqmqKtQ0k1w==","signatures":[{"sig":"MEUCIQDbckI/YHAIaOG2tmhzL1yTAwWgUEgI+Na6zH2tTd0KLQIgXeEzAjM/rLZhC26mDLJcQKExRTbVEeis+PD9wxtsC5g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIEhyuiAVka8CVM4+X72B95gMud+sQc1WvshsDz6TiaCDAiB7WOMgImv3nJtagGLiKGA/7HnJ04stgbZ/AGhkqh6heQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":259166},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./global":"./dist/poke.global.js","./package.json":"./package.json"},"gitHead":"d45a3e5d39d8918cf945f09b8cd839d5dfd7b7d7","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"jsdelivr":"./dist/poke.global.js","repository":{"url":"git+https://github.com/yusuf-ishaku/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/poke_0.2.0_1788916803597_0.2403928987490076","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@appliftlabs/poke","version":"0.3.0","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"author":{"name":"Applift Labs"},"license":"MIT","_id":"@appliftlabs/poke@0.3.0","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"homepage":"https://github.com/yusuf-ishaku/poke#readme","bugs":{"url":"https://github.com/yusuf-ishaku/poke/issues"},"dist":{"shasum":"297bbbafecbce0f1f9ed4b7a475a621896d6a499","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.3.0.tgz","fileCount":8,"integrity":"sha512-W0eAQV5i/hppctnwMVP9tTKVhAWMWFxoGa+7HeMs1iK+uSMOJrbZJAYgCnptOAvjCEuZRCYd0JBqbEUUaXTwaw==","signatures":[{"sig":"MEYCIQDHp7A5XAgxXa3JKOIkfwGjlW+PYLLjb5TkFw9LhLZsjgIhAMzpeHAbmmJ8DBws0mGNCN1NyUaUUmPEN9+y7bjD/IeG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIHBR59vEIXxRcC1c84VwXTU3mGVUlU0EeOV9KyJAuj0EAiEAnNRofZuMAuB/v9gHZj04NIikHqBFje/5hMY9VshtpLQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":265758},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./global":"./dist/poke.global.js","./package.json":"./package.json"},"gitHead":"c9a2e0f49f28cacf49269eaad120d00584315a96","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"jsdelivr":"./dist/poke.global.js","repository":{"url":"git+https://github.com/yusuf-ishaku/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2"},"_npmOperationalInternal":{"tmp":"tmp/poke_0.3.0_1788917735575_0.4336570586375399","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@appliftlabs/poke","version":"0.4.0","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"author":{"name":"Applift Labs"},"license":"MIT","_id":"@appliftlabs/poke@0.4.0","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"homepage":"https://github.com/yusuf-ishaku/poke#readme","bugs":{"url":"https://github.com/yusuf-ishaku/poke/issues"},"dist":{"shasum":"d56a9d92c02c9fc70c828ccf30cfcf7688c5ad0f","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.4.0.tgz","fileCount":20,"integrity":"sha512-dWXPxXVWz9WIySxDNl/kqyFO+hVJY6Kt0ngnAsn04fiqTP9PVZl4CrgcXwCuXkzWl1CJQDXpZvk3feiBx9Faqw==","signatures":[{"sig":"MEUCIQD6y/MQ38fyDmxHfwEGBv55fPhp7oWJyGrrY1ORifzM9AIgWYdjS3Dd4qaqMF0YiOZNlObwBdkNBwro1SXvV3gxOO8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQD/T+tDjrnTSt0BFZ10yOvurUWcHYJoc9kfLBS+gtkjQAIhAJ5avKqS9/PGO3rsPXAEUYX+go+0hGcz8VXxDExRgf1/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":325122},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js","require":"./dist/node.cjs"},"./global":"./dist/poke.global.js","./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"},"./next-js":{"types":"./dist/next-js.d.ts","import":"./dist/next-js.js","require":"./dist/next-js.cjs"},"./package.json":"./package.json"},"gitHead":"7fb66b333c59279a3ae06f36e95259445678e7ea","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"jsdelivr":"./dist/poke.global.js","repository":{"url":"git+https://github.com/yusuf-ishaku/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"pg":"^8.23.0","tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","@types/pg":"^8.23.1","typescript":"^5.7.2","@types/node":"^22.10.2"},"peerDependencies":{"pg":"^8.11.0"},"peerDependenciesMeta":{"pg":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/poke_0.4.0_1789656591425_0.2952958064374118","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@appliftlabs/poke","version":"0.4.1","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"author":{"name":"Applift Labs"},"license":"MIT","_id":"@appliftlabs/poke@0.4.1","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"homepage":"https://github.com/yusuf-ishaku/poke#readme","bugs":{"url":"https://github.com/yusuf-ishaku/poke/issues"},"dist":{"shasum":"36c4e2b598b5398249dc8ed248aedfc49f567ae2","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.4.1.tgz","fileCount":20,"integrity":"sha512-D3JdIamcTotNMBko2XUYhVuX5jKgwMuztIygSlgA4nr7gua1CNr2NXo0uBv7j4k+oDCH+7jB9Em9qxG2mJvlAg==","signatures":[{"sig":"MEUCIA93prF27+6iOaRuwPTmvWQm+d5kyTA1yq0Q8q04MA5TAiEAog9iTiW4dy6kgfI2ilVw+7dKA+J83lVd8pgojZMFUHI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDT4eS3flPJp59FL4LLJUarFMR5hdSoyaPH8YFoj234MwIgG8NSPBq2PWL8m0Pl/6yyUJIvCIdBagPNmHctWr+79kM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":316104},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js","require":"./dist/node.cjs"},"./global":"./dist/poke.global.js","./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"},"./next-js":{"types":"./dist/next-js.d.ts","import":"./dist/next-js.js","require":"./dist/next-js.cjs"},"./package.json":"./package.json"},"gitHead":"b8009b6dcc49aa0e75f7d90b8f794cee03465f15","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"jsdelivr":"./dist/poke.global.js","repository":{"url":"git+https://github.com/yusuf-ishaku/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"pg":"^8.23.0","tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","@types/pg":"^8.23.1","typescript":"^5.7.2","@types/node":"^22.10.2"},"peerDependencies":{"pg":"^8.11.0"},"peerDependenciesMeta":{"pg":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/poke_0.4.1_1789657466398_0.6602392258370346","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"_id":"@appliftlabs/poke@0.4.2","bugs":{"url":"https://github.com/appliftlabs/poke/issues"},"dist":{"shasum":"2dff36a2f4ed3e5ff65fe5f298eada9aae1ca95c","tarball":"https://registry.npmjs.org/@appliftlabs/poke/-/poke-0.4.2.tgz","fileCount":20,"integrity":"sha512-mxeVAz70x6RQdUpQOZJjCjPEOJxY3x9QnU60WTFlv6KvrVhgBl38qDswehscwr9i4MDFrwNq/1t7x/4B6AAjIg==","signatures":[{"sig":"MEYCIQD5LT4/pEA7qTu/LBgNqstPODdpxre3r3TTwHE3AkvCvgIhAK0rdKu9boLTU3JlY0mfe8EqDdv5RuKkdUtTCxkdyNSI","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCSJMBl9jjgdcAuAbuutWf/7mm57EIaE9LwlFDjn79ktQIgJimEeqYueWxhXGWdDZqUzYcKdkcY33ColiCT6KpdMXQ="}],"unpackedSize":316099},"main":"./dist/index.cjs","name":"@appliftlabs/poke","type":"module","types":"./dist/index.d.ts","unpkg":"./dist/poke.global.js","author":{"name":"Applift Labs"},"module":"./dist/index.js","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js","require":"./dist/node.cjs"},"./global":"./dist/poke.global.js","./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"},"./next-js":{"types":"./dist/next-js.d.ts","import":"./dist/next-js.js","require":"./dist/next-js.cjs"},"./package.json":"./package.json"},"gitHead":"f14389c7e5e17a9198fddba05977b87f11e2b3d9","license":"MIT","scripts":{"dev":"tsup --watch","lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"version":"0.4.2","_npmUser":{"name":"ishakufortune","email":"kingshakzy@gmail.com"},"homepage":"https://github.com/appliftlabs/poke#readme","jsdelivr":"./dist/poke.global.js","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"repository":{"url":"git+https://github.com/appliftlabs/poke.git","type":"git"},"_npmVersion":"11.5.1","description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","directories":{},"maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.7.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"pg":"^8.23.0","tsup":"^8.3.5","jsdom":"^25.0.1","preact":"^10.25.3","vitest":"^2.1.8","@types/pg":"^8.23.1","typescript":"^5.7.2","@types/node":"^22.10.2"},"peerDependencies":{"pg":"^8.11.0"},"peerDependenciesMeta":{"pg":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/poke_0.4.2_1789745910253_0.5587493959186247"}}},"time":{"created":"2026-09-08T23:47:42.595Z","modified":"2026-09-18T15:38:30.548Z","0.1.0":"2026-09-08T23:47:42.951Z","0.1.1":"2026-09-09T00:34:12.514Z","0.2.0":"2026-09-09T01:20:03.719Z","0.3.0":"2026-09-09T01:35:35.657Z","0.4.0":"2026-09-17T14:49:51.511Z","0.4.1":"2026-09-17T15:04:26.500Z","0.4.2":"2026-09-18T15:38:30.344Z"},"bugs":{"url":"https://github.com/appliftlabs/poke/issues"},"author":{"name":"Applift Labs"},"license":"MIT","homepage":"https://github.com/appliftlabs/poke#readme","keywords":["comments","annotations","feedback","bug-reporting","review","figma-style"],"repository":{"url":"git+https://github.com/appliftlabs/poke.git","type":"git"},"description":"Figma-style pinned comments for any live web page. Drop-in, element-anchored, open source.","maintainers":[{"name":"ishakufortune","email":"kingshakzy@gmail.com"}],"readme":"# Poke\n\n**Figma-style pinned comments for any live web page.**\n\nClick something, leave a note, it stays pinned to that exact element — even\nafter the page reloads or the layout shifts. No more \"the button, you know,\nthe blue one, near the top.\" Open source, MIT licensed, from\n[Applift Labs](https://applift.xyz).\n\n```bash\nnpm install @appliftlabs/poke\n```\n\n## Quick start: comments backed by your own database\n\nThe fastest path to real, multi-user comments is running Poke's API as a route\ninside the backend you already have — no separate service, no second\ndatabase.\n\n```ts\n// app/api/poke/[...poke]/route.ts  (Next.js App Router)\nimport { createPoke } from \"@appliftlabs/poke/server\";\nimport { toNextJsHandler } from \"@appliftlabs/poke/next-js\";\nimport { Pool } from \"pg\";\n\nconst poke = createPoke({\n  database: new Pool({ connectionString: process.env.DATABASE_URL }), // your existing pool\n  getUser: async () => {\n    const session = await auth(); // your existing auth\n    return session && { id: session.user.id, name: session.user.name };\n  },\n});\n\nexport const { GET, POST, PATCH, DELETE } = toNextJsHandler(poke.handler);\n```\n\n```tsx\n// app/poke.tsx — the client half, mounted once near your app root\n\"use client\";\nimport { useEffect } from \"react\";\nimport { usePathname } from \"next/navigation\";\n\nexport function Poke() {\n  const pathname = usePathname();\n  useEffect(() => {\n    let instance: { destroy(): void } | undefined;\n    import(\"@appliftlabs/poke\").then(({ init, HttpAdapter }) => {\n      instance = init({\n        enabled: process.env.NODE_ENV !== \"production\", // never ships to real users\n        pageId: pathname,\n        adapter: new HttpAdapter({ baseUrl: \"/api/poke\" }),\n      });\n    });\n    return () => instance?.destroy();\n  }, [pathname]);\n  return null;\n}\n```\n\nThat's a whole team commenting live on your app, stored in Postgres you\nalready run. `createPoke` needs `pg` (`npm i pg`) — a peer dependency, not\nbundled, so it uses your own driver. The schema creates itself on first\nrequest.\n\n**Not Next.js?** Same `createPoke()`, a different one-line mount:\n\n```ts\nimport { createServer } from \"node:http\";\nimport { createPoke } from \"@appliftlabs/poke/server\";\nimport { toNodeHandler } from \"@appliftlabs/poke/node\";\nimport { Pool } from \"pg\";\n\nconst poke = createPoke({\n  database: new Pool({ connectionString: process.env.DATABASE_URL }),\n});\ncreateServer(toNodeHandler(poke.handler)).listen(4000);\n```\n\n**Not Node at all, or want it fully isolated from your app?** See\n[Standalone server](#standalone-server-non-nodejs-backends) below.\n\n**Other frameworks** (React, Vue, Svelte, plain HTML) — the client-side half\nabove is nearly identical everywhere; see\n[`examples/frameworks/`](https://github.com/appliftlabs/poke/tree/main/examples/frameworks)\nfor a complete file per stack.\n\n## Or skip the backend entirely\n\nNo `adapter`, no server, nothing to deploy — comments save to `localStorage`\nin that one browser. Good for a solo pass over your own app before you wire up\nthe real thing.\n\n```js\nimport { init } from \"@appliftlabs/poke\";\n\ninit({\n  enabled: process.env.NODE_ENV !== \"production\",\n  user: { id: currentUser.id, name: currentUser.name }, // or omit for a name prompt\n});\n```\n\nPreact is bundled in and Poke renders into its own Shadow DOM, so this is the\nentire install — nothing else to add, nothing of yours it can collide with.\n\n`init()` returns `{ store, identity, mount, unmount, destroy }`. Call\n`destroy()` on teardown.\n\n## How it works\n\n**The client** (`@appliftlabs/poke`) is the part that runs in the browser: a\ncomment-mode toggle, click-to-pin, threaded replies, resolve/reopen, and a\nsidebar listing every comment across your whole app. It always runs, whether\nor not you've set up a backend.\n\n**A backend** is only needed once two people need to see each other's\ncomments — the browser can't safely talk to a database on its own. Three ways\nto get one, in order of how little you have to do:\n\n1. **`createPoke()` inside your app** (above) — the default recommendation.\n2. **[Standalone server](#standalone-server-non-nodejs-backends)** — a small\n   Postgres-backed server you deploy separately.\n3. **Your own `StorageAdapter`** — implement six methods against Supabase,\n   Firebase, or an API you already have. See\n   [Writing a custom adapter](#writing-a-custom-adapter).\n\n## Keeping Poke out of production\n\nPoke is a review tool — it shouldn't reach real users. Gate it with `enabled`:\n\n```js\ninit({ enabled: process.env.NODE_ENV !== \"production\" }); // Node / Next\ninit({ enabled: import.meta.env.DEV });                    // Vite\n```\n\nAnything other than `true` or a dev-ish string (`\"development\"`, `\"staging\"`,\n`\"test\"`, `\"preview\"`, `\"local\"`) makes `init()` a complete no-op — no UI, no\nnetwork — and it still returns a valid instance so your code doesn't need a\nguard around it.\n\n## Identity\n\n| You pass | Author of comments | Name prompt |\n|---|---|---|\n| `user: { id, name }` | that user | never |\n| `user: { name: \"Sam\" }` | a stable per-browser id, name pre-filled | never |\n| nothing | a stable per-browser id | on first comment, then remembered |\n\nNot authentication — anyone can type any name — but the right weight for \"a\nclient opens a link and leaves feedback.\" `init()` returns an `identity`\nhandle (`identity.name`, `identity.setName(...)`) if you want to drive it\nyourself.\n\n## `createPoke` reference\n\n```ts\ncreatePoke({\n  database,        // required: a pg.Pool, or anything shaped like one\n  getUser,         // (req) => PokeUser | null — derive the author from your session\n  allowAnonymous,  // trust the client's own author instead of getUser. default false\n  project,         // namespace comments when one DB serves multiple apps. default \"default\"\n  origins,         // CORS allowlist: \"*\", an exact origin, or \"https://*.example.com\". default \"*\"\n  realtime,        // SSE live updates. default true — see note below\n})\n```\n\n<details>\n<summary>Realtime on serverless/edge platforms</summary>\n\nThe live-update stream is in-process: a write on one server instance notifies\nsubscribers connected to *that instance*. Exactly right for a single\nlong-lived Node process. On serverless/edge platforms that run many\nshort-lived instances, two people on different instances won't see each\nother's comments appear live — reads are always correct, just not always\npushed instantly. Set `realtime: false` to skip the SSE route where long\nconnections don't work; viewers pick up changes on their next navigation.\n\n</details>\n\n## Standalone server (non-Node.js backends)\n\nA ~350-line Postgres-backed server you deploy on its own — for a non-Node\nbackend, or when you'd rather keep Poke fully separate from your app. Full\ninstructions (Railway, Fly, Docker) are in\n[`server/README.md`](https://github.com/appliftlabs/poke/blob/main/server/README.md).\n\n```bash\ncd server && npm install\ndocker compose up -d                              # or bring your own Postgres\nDATABASE_URL=postgres://poke:poke@localhost:5432/poke npm run migrate\nnode --env-file=.env src/server.js                 # http://localhost:4000\n```\n\nPoint the client at it the same way, just with a full URL:\n\n```js\nadapter: new HttpAdapter({ baseUrl: \"https://your-poke-server.example.com\" })\n```\n\n<details>\n<summary>The REST contract (for either backend, or a custom one)</summary>\n\n| Method | Path | Body | Returns |\n|---|---|---|---|\n| `GET` | `/pages/:pageId/threads` | — | `PokeThread[]` (one page) |\n| `GET` | `/threads` | — | `PokeThread[]` (whole app — the sidebar) |\n| `POST` | `/threads` | `PokeThread` | `201` |\n| `POST` | `/threads/:id/messages` | `{ body, author }` | `PokeMessage` |\n| `PATCH` | `/threads/:id` | `{ status }` | `200` |\n| `PATCH` | `/messages/:id` | `{ body }` | `200` |\n| `DELETE` | `/threads/:id` | — | `204` |\n| `GET` | `/pages/:pageId/events` | — | SSE stream (`:pageId` may be `*` for every page) |\n\n</details>\n\n## Writing a custom adapter\n\nImplement `StorageAdapter` against Supabase, Firebase, or your own API — six\nmethods (all may be async) plus an optional `subscribe()` for realtime:\n\n```ts\nimport type { StorageAdapter } from \"@appliftlabs/poke\";\n\nclass FirebaseAdapter implements StorageAdapter {\n  listThreads(pageId) { /* query */ }\n  createThread(thread) { /* write */ }\n  addMessage({ threadId, body, author }) { /* append, return the new PokeMessage */ }\n  updateThread(id, patch) { /* patch { status } */ }\n  updateMessage(id, body) { /* patch body */ }\n  deleteThread(id) { /* delete */ }\n  subscribe(pageId, listener) {\n    // call listener({ type: \"reload\" }) on any remote change; return unsub\n  }\n}\n```\n\n## The hard part: element anchoring\n\nA naive version of this stores `{ x: 320, y: 78 }` and draws a pin there. That\nbreaks the moment the page reflows or the viewport changes. Poke instead\nremembers *which element* a comment belongs to.\n\nWhen you drop a pin, Poke captures an `ElementAnchor` — a redundant\ndescription of the target: a CSS selector that prefers stable hooks (`id`,\n`data-testid`) over hashed/generated ones, a structural path\n(`body > main > ul > li:3`) that survives class churn, identifying attributes,\ntrimmed text content, and where *within* the element you clicked. On the next\nload, `resolveAnchor()` scores candidate elements against all of that evidence\nat once — any single signal can go stale and the pin still lands, because the\nothers corroborate.\n\n```ts\nimport { captureAnchor, resolveAnchor, anchorPoint } from \"@appliftlabs/poke\";\n\nconst anchor = captureAnchor(clickedElement, { clientX, clientY });\nsaveToYourStore(comment, anchor); // anchor is plain JSON\n\n// later, on any page load:\nconst { element, confidence } = resolveAnchor(anchor);\nif (element) {\n  const { x, y } = anchorPoint(element, anchor);\n  drawPinAt(x, y); // confidence: \"exact\" | \"high\" | \"medium\" | \"low\" | \"lost\"\n}\n```\n\n## Development\n\n```bash\nnpm install\nnpm test           # vitest — jsdom for the client, real Postgres for src/server (opt-in)\nnpm run typecheck\nnpm run build       # dist/ — ESM, CJS, a script-tag IIFE, and the /server, /next-js, /node entries\n```\n\n`examples/demo.html` is the full comment layer on a sample page.\n`examples/anchor-playground.html` shuffles the DOM live to show re-anchoring.\n`examples/frameworks/` has one runnable file per framework.\n\n## License\n\nMIT © Applift Labs\n","readmeFilename":"README.md"}