feedtack 0.5.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # feedtack
2
2
 
3
- > Click anywhere. Drop a pin. Get a payload a developer can act on.
3
+ > Click anywhere. Drop a pin. Leave a note. Get a payload a developer can act on.
4
4
 
5
- **feedtack** is a drop-in React feedback overlay. Non-technical stakeholders click anywhere on a page, leave a comment, and feedtack emits a structured JSON payload so complete that an LLM can attempt a first-pass fix before consuming developer hours.
5
+ **feedtack** is a drop-in React feedback overlay. A "Feedback" button opens a modal where anyone can leave site-wide notes, page-level comments, or place a pin on a specific element — all from one entry point. feedtack emits a structured JSON payload so complete that an LLM can attempt a first-pass fix before consuming developer hours.
6
6
 
7
7
  ## Install
8
8
 
@@ -166,16 +166,19 @@ class SupabaseAdapter implements FeedtackAdapter {
166
166
 
167
167
  ## The payload
168
168
 
169
- Every pin emits a versioned JSON payload:
169
+ Every submission emits a versioned JSON payload. The `scope` field indicates where the feedback lives.
170
+
171
+ **Element-scoped (pinned to a specific element):**
170
172
 
171
173
  ```json
172
174
  {
173
- "schemaVersion": "1.0.0",
175
+ "schemaVersion": "2.0.0",
174
176
  "id": "ft_01j...",
175
177
  "timestamp": "2026-04-09T13:42:00.000Z",
176
178
  "submittedBy": { "id": "u1", "name": "Alice", "role": "designer" },
179
+ "scope": "element",
177
180
  "comment": "This button doesn't do anything",
178
- "sentiment": "dissatisfied",
181
+ "sentiment": "bad",
179
182
  "pins": [{
180
183
  "index": 1,
181
184
  "color": "#ef4444",
@@ -185,8 +188,8 @@ Every pin emits a versioned JSON payload:
185
188
  "selector": "#submit-btn",
186
189
  "best_effort": false,
187
190
  "tagName": "BUTTON",
188
- "textContent": "Place Order",
189
- "attributes": { "id": "submit-btn", "disabled": "true" },
191
+ "dataTestId": "submit-btn",
192
+ "ancestors": ["form#checkout", "main"],
190
193
  "boundingRect": { "x": 420, "y": 812, "width": 200, "height": 44 }
191
194
  }
192
195
  }],
@@ -196,14 +199,47 @@ Every pin emits a versioned JSON payload:
196
199
  }
197
200
  ```
198
201
 
202
+ **Page or site-scoped (no pin):**
203
+
204
+ ```json
205
+ {
206
+ "schemaVersion": "2.0.0",
207
+ "id": "ft_01k...",
208
+ "scope": "page",
209
+ "comment": "This page is really confusing — too many steps",
210
+ "sentiment": "bad",
211
+ "pins": [],
212
+ "page": { "url": "https://app.example.com/checkout", "pathname": "/checkout", "title": "Checkout" },
213
+ ...
214
+ }
215
+ ```
216
+
217
+ `sentiment` values: `"good"` | `"bad"` | `null`
218
+
219
+ ## Feedback scopes
220
+
221
+ Feedtack supports three levels of feedback, all accessible from the modal:
222
+
223
+ | Scope | When to use | Pins |
224
+ |-------|-------------|------|
225
+ | `site` | Config-level feedback affecting the whole site (e.g. "change the font everywhere") | None |
226
+ | `page` | Feedback specific to the current page (e.g. "this page is confusing") | None |
227
+ | `element` | Feedback pinned to a specific element (e.g. "this button doesn't work") | One or more |
228
+
229
+ **How it works:**
230
+ - Clicking "Feedback" opens the modal
231
+ - The modal has **Site** and **Page** tabs for scope-level feedback
232
+ - "Place a pin" in the modal footer activates crosshair mode — useful on mobile too
233
+ - `Shift+P` anywhere on the page opens the modal
234
+
199
235
  ## `useFeedtack` hook
200
236
 
201
237
  ```tsx
202
238
  import { useFeedtack } from 'feedtack/react'
203
239
 
204
240
  function MyButton() {
205
- const { activatePinMode, isPinModeActive } = useFeedtack()
206
- return <button onClick={activatePinMode}>{isPinModeActive ? 'Cancel' : 'Give Feedback'}</button>
241
+ const { openModal, closeModal, isModalOpen, activatePinMode, isPinModeActive } = useFeedtack()
242
+ return <button onClick={openModal}>{isModalOpen ? 'Close' : 'Give Feedback'}</button>
207
243
  }
208
244
  ```
209
245
 
@@ -200,8 +200,8 @@ function getTargetMeta(element) {
200
200
  tagName: resolved.tagName,
201
201
  ancestors,
202
202
  boundingRect: {
203
- x: rect.x,
204
- y: rect.y,
203
+ x: rect.x + window.scrollX,
204
+ y: rect.y + window.scrollY,
205
205
  width: rect.width,
206
206
  height: rect.height
207
207
  }
@@ -209,7 +209,7 @@ function getTargetMeta(element) {
209
209
  }
210
210
 
211
211
  // src/types/payload.ts
212
- var SCHEMA_VERSION = "1.0.0";
212
+ var SCHEMA_VERSION = "2.0.0";
213
213
 
214
214
  // src/types/theme.ts
215
215
  function themeToCSS(theme) {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FeedtackAdapter, a as FeedtackPayload, b as FeedtackReply, c as FeedtackResolution, d as FeedtackFilter, e as FeedbackItem, f as FeedtackDeviceMeta, g as FeedtackPageMeta, h as FeedtackViewportMeta, i as FeedtackPinTarget } from './theme-C-uctIoI.js';
2
- export { A as AncestorNode, j as FeedtackArchive, k as FeedtackBoundingRect, l as FeedtackPin, m as FeedtackSentiment, n as FeedtackTheme, o as FeedtackUser, S as SCHEMA_VERSION, t as themeToCSS } from './theme-C-uctIoI.js';
1
+ import { F as FeedtackAdapter, a as FeedtackPayload, b as FeedtackReply, c as FeedtackResolution, d as FeedtackFilter, e as FeedbackItem, f as FeedtackDeviceMeta, g as FeedtackPageMeta, h as FeedtackViewportMeta, i as FeedtackPinTarget } from './theme-BdqpMipn.js';
2
+ export { A as AncestorNode, j as FeedtackArchive, k as FeedtackBoundingRect, l as FeedtackPin, m as FeedtackScope, n as FeedtackSentiment, o as FeedtackTheme, p as FeedtackUser, S as SCHEMA_VERSION, t as themeToCSS } from './theme-BdqpMipn.js';
3
3
 
4
4
  /** Development adapter — logs all operations to the browser console */
5
5
  declare class ConsoleAdapter implements FeedtackAdapter {
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  getTargetMeta,
8
8
  getViewportMeta,
9
9
  themeToCSS
10
- } from "./chunk-NCW2V5JL.js";
10
+ } from "./chunk-2A5LLDLP.js";
11
11
 
12
12
  // src/adapters/ConsoleAdapter.ts
13
13
  var ConsoleAdapter = class {
@@ -83,6 +83,7 @@ var LocalStorageAdapter = class {
83
83
  const items = this.read();
84
84
  if (!filter) return items;
85
85
  return items.filter((item) => {
86
+ if (filter.scope && item.payload.scope !== filter.scope) return false;
86
87
  if (filter.pathname && item.payload.page.pathname !== filter.pathname)
87
88
  return false;
88
89
  if (filter.url && item.payload.page.url !== filter.url) return false;
@@ -1,6 +1,22 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
- import { e as FeedbackItem, F as FeedtackAdapter, o as FeedtackUser, n as FeedtackTheme } from '../theme-C-uctIoI.js';
3
+ import { e as FeedbackItem, F as FeedtackAdapter, p as FeedtackUser, o as FeedtackTheme } from '../theme-BdqpMipn.js';
4
+
5
+ /** Fixed palette of 6 colors for pin markers */
6
+ declare const PIN_PALETTE: readonly ["#ef4444", "#3b82f6", "#22c55e", "#f59e0b", "#a855f7", "#ec4899"];
7
+ type PinColor = (typeof PIN_PALETTE)[number];
8
+
9
+ interface FeedtackContextValue {
10
+ activatePinMode: () => void;
11
+ deactivatePinMode: () => void;
12
+ isPinModeActive: boolean;
13
+ selectedColor: string;
14
+ setSelectedColor: (color: string) => void;
15
+ pinPalette: readonly string[];
16
+ openModal: () => void;
17
+ closeModal: () => void;
18
+ isModalOpen: boolean;
19
+ }
4
20
 
5
21
  interface FeedtackFlushEvent {
6
22
  pathname: string;
@@ -40,13 +56,7 @@ interface FeedtackProviderProps {
40
56
  }
41
57
  declare function FeedtackProvider({ children, adapter, currentUser, hotkey, adminOnly, theme, classes, sentimentLabels, onError, disabled, renderPinIcon, onFlush, flushIdleMs, rescopeRoles, }: FeedtackProviderProps): react_jsx_runtime.JSX.Element;
42
58
 
43
- interface FeedtackContextValue {
44
- activatePinMode: () => void;
45
- deactivatePinMode: () => void;
46
- isPinModeActive: boolean;
47
- }
48
-
49
59
  /** Hook for host app to programmatically control feedtack */
50
60
  declare function useFeedtack(): FeedtackContextValue;
51
61
 
52
- export { type FeedtackClasses, type FeedtackFlushEvent, FeedtackProvider, type FeedtackProviderProps, type FeedtackSentimentLabels, useFeedtack };
62
+ export { type FeedtackClasses, type FeedtackContextValue, type FeedtackFlushEvent, FeedtackProvider, type FeedtackProviderProps, type FeedtackSentimentLabels, PIN_PALETTE, type PinColor, useFeedtack };