@sirendesign/markup 1.0.0

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 ADDED
@@ -0,0 +1,329 @@
1
+ # @siren/markup
2
+
3
+ A powerful web markup and feedback tool for collecting visual feedback from clients and account managers. Similar to [marker.io](https://marker.io), this tool provides an easy-to-use widget for capturing screenshots, adding annotations, and managing feedback items.
4
+
5
+ ## ✨ New: Firebase Integration
6
+
7
+ **Multi-tenant feedback with authentication and real-time sync!** See [FIREBASE_SETUP.md](./FIREBASE_SETUP.md) for complete setup instructions.
8
+
9
+ - 🔒 **User Authentication** - Email/password authentication via Firebase
10
+ - ☁️ **Cloud Storage** - Feedback stored in Firestore with real-time sync
11
+ - 🏢 **Multi-Tenancy** - Each website/project has isolated feedback
12
+ - 🔄 **Real-time Updates** - Changes sync instantly across all devices
13
+ - 👥 **User Tracking** - Know who submitted each ticket
14
+
15
+ ## Features
16
+
17
+ - 📸 **Screenshot Capture** - Capture full-page screenshots with one click
18
+ - ✏️ **Annotation Tools** - Add arrows, rectangles, circles, highlights, text, and freehand drawings
19
+ - ✂️ **Copy Amendments** - Track text changes with before/after comparison
20
+ - 💬 **Feedback Management** - Create, view, and manage feedback items
21
+ - 🏷️ **Tags & Priority** - Organize feedback with custom tags and priority levels
22
+ - 📊 **Status Tracking** - Track feedback status (Open, In Progress, Resolved, Closed)
23
+ - 💾 **Local or Cloud Storage** - Works offline or with Firebase sync
24
+ - 🔌 **API Integration** - Optional API endpoint for server-side storage
25
+ - ⌨️ **Keyboard Shortcuts** - Quick access with customizable shortcuts
26
+ - 🎨 **Customizable** - Theming, labels, and position options
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install @siren/markup firebase
32
+ # or
33
+ yarn add @siren/markup firebase
34
+ # or
35
+ pnpm add @siren/markup firebase
36
+ ```
37
+
38
+ Note: `firebase` is included as a peer dependency if you want to use authentication and cloud sync.
39
+
40
+ ## Quick Start
41
+
42
+ ### Basic Setup (Local Storage)
43
+
44
+ ```tsx
45
+ import { MarkupWidget } from '@siren/markup';
46
+ import '@siren/markup/dist/styles.css';
47
+
48
+ function App() {
49
+ return (
50
+ <div>
51
+ <YourApp />
52
+
53
+ <MarkupWidget
54
+ config={{
55
+ projectId: 'my-project',
56
+ primaryColor: '#6366f1',
57
+ }}
58
+ />
59
+ </div>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ### With Firebase (Authentication & Cloud Sync)
65
+
66
+ ```tsx
67
+ import { MarkupWidget, initFirebase } from '@siren/markup';
68
+ import '@siren/markup/dist/styles.css';
69
+
70
+ // 1. Create .env in YOUR app (not the npm package):
71
+ // VITE_FIREBASE_API_KEY=...
72
+ // VITE_FIREBASE_AUTH_DOMAIN=...
73
+ // etc.
74
+
75
+ // 2. YOUR app reads .env and calls initFirebase once
76
+ initFirebase({
77
+ apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
78
+ authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
79
+ projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
80
+ storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
81
+ messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
82
+ appId: import.meta.env.VITE_FIREBASE_APP_ID,
83
+ });
84
+
85
+ function App() {
86
+ return (
87
+ <div>
88
+ <YourApp />
89
+
90
+ <MarkupWidget config={{ projectId: 'my-website-prod' }} />
91
+ </div>
92
+ );
93
+ }
94
+ ```
95
+
96
+ **With Firebase enabled:**
97
+ - ✅ Users must sign in to submit feedback
98
+ - ✅ Feedback syncs across devices in real-time
99
+ - ✅ Each `projectId` has isolated feedback
100
+ - ✅ Track who submitted each ticket
101
+
102
+ See [FIREBASE_SETUP.md](./FIREBASE_SETUP.md) for complete setup instructions.
103
+
104
+ ### Next.js App Router
105
+
106
+ For Next.js 13+ with the App Router, create a client component:
107
+
108
+ ```tsx
109
+ // components/FeedbackWidget.tsx
110
+ 'use client';
111
+
112
+ import { MarkupWidget } from '@siren/markup';
113
+ import '@siren/markup/dist/styles.css';
114
+
115
+ export default function FeedbackWidget() {
116
+ return (
117
+ <MarkupWidget
118
+ config={{
119
+ projectId: 'my-project',
120
+ id: 'user-123',
121
+ name: 'John Doe',
122
+ email: 'john@example.com',
123
+ },
124
+ }}
125
+ />
126
+ );
127
+ }
128
+ ```
129
+
130
+ Then use it in your layout:
131
+
132
+ ```tsx
133
+ // app/layout.tsx
134
+ import FeedbackWidget from '@/components/FeedbackWidget';
135
+
136
+ export default function RootLayout({ children }) {
137
+ return (
138
+ <html>
139
+ <body>
140
+ {children}
141
+ <FeedbackWidget />
142
+ </body>
143
+ </html>
144
+ );
145
+ }
146
+ ```
147
+
148
+ ## Configuration Options
149
+
150
+ ```typescript
151
+ interface MarkupConfig {
152
+ // Required: Project identifier
153
+ projectId: string;
154
+
155
+ // User information (optional)
156
+ user?: {
157
+ id: string;
158
+ name: string;
159
+ email: string;
160
+ avatar?: string;
161
+ role?: 'admin' | 'developer' | 'client' | 'account-manager';
162
+ };
163
+
164
+ // API endpoint for storing feedback (uses local storage if not provided)
165
+ apiEndpoint?: string;
166
+
167
+ // API key for authentication
168
+ apiKey?: string;
169
+
170
+ // Widget position (default: 'bottom-right')
171
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
172
+
173
+ // Primary color (default: '#6366f1')
174
+ primaryColor?: string;
175
+
176
+ // Whether widget opens by default
177
+ defaultOpen?: boolean;
178
+
179
+ // Keyboard shortcut (default: 'ctrl+shift+m')
180
+ shortcut?: string;
181
+
182
+ // Custom labels
183
+ labels?: {
184
+ widgetButton?: string;
185
+ feedbackTitle?: string;
186
+ submitButton?: string;
187
+ cancelButton?: string;
188
+ };
189
+
190
+ // Callback when feedback is submitted
191
+ onSubmit?: (feedback: FeedbackItem) => void | Promise<void>;
192
+
193
+ // Callback when status changes
194
+ onStatusChange?: (feedbackId: string, status: FeedbackStatus) => void | Promise<void>;
195
+
196
+ // Tags available for feedback
197
+ availableTags?: string[];
198
+
199
+ // Team members for assignment
200
+ teamMembers?: UserInfo[];
201
+ }
202
+ ```
203
+
204
+ ## API Integration
205
+
206
+ To store feedback on your server, provide an `apiEndpoint`:
207
+
208
+ ```tsx
209
+ <MarkupWidget
210
+ config={{
211
+ projectId: 'my-project',
212
+ apiEndpoint: 'https://api.yourserver.com',
213
+ apiKey: 'your-api-key',
214
+ onSubmit: async (feedback) => {
215
+ // Custom handling after submission
216
+ console.log('Feedback submitted:', feedback);
217
+ },
218
+ }}
219
+ />
220
+ ```
221
+
222
+ ### Expected API Endpoints
223
+
224
+ If you provide an `apiEndpoint`, implement these endpoints:
225
+
226
+ - `GET /projects/:projectId/feedback` - List all feedback
227
+ - `POST /projects/:projectId/feedback` - Create feedback
228
+ - `PATCH /projects/:projectId/feedback/:id` - Update feedback
229
+ - `DELETE /projects/:projectId/feedback/:id` - Delete feedback
230
+ - `POST /projects/:projectId/feedback/:id/comments` - Add comment
231
+ - `POST /projects/:projectId/upload` - Upload screenshot (returns `{ url: string }`)
232
+
233
+ ## Using the Store Directly
234
+
235
+ Access the feedback store for custom integrations:
236
+
237
+ ```tsx
238
+ import { useMarkupStore } from '@company/markup';
239
+
240
+ function AdminDashboard() {
241
+ const { feedbackItems, updateFeedbackItem } = useMarkupStore();
242
+
243
+ const openItems = feedbackItems.filter(item => item.status === 'open');
244
+
245
+ return (
246
+ <div>
247
+ <h2>Open Feedback ({openItems.length})</h2>
248
+ {openItems.map(item => (
249
+ <div key={item.id}>
250
+ <h3>{item.title}</h3>
251
+ <button onClick={() => updateFeedbackItem(item.id, { status: 'in-progress' })}>
252
+ Start Working
253
+ </button>
254
+ </div>
255
+ ))}
256
+ </div>
257
+ );
258
+ }
259
+ ```
260
+
261
+ ## Customization
262
+
263
+ ### Custom Styling
264
+
265
+ Override CSS variables for custom theming:
266
+
267
+ ```css
268
+ .markup-widget {
269
+ --markup-primary: #your-brand-color;
270
+ --markup-primary-hover: #your-brand-color-darker;
271
+ --markup-bg: #ffffff;
272
+ --markup-bg-secondary: #f3f4f6;
273
+ --markup-border: #e5e7eb;
274
+ --markup-text: #1f2937;
275
+ --markup-text-secondary: #6b7280;
276
+ --markup-radius: 12px;
277
+ }
278
+ ```
279
+
280
+ ### Custom Labels
281
+
282
+ ```tsx
283
+ <MarkupWidget
284
+ config={{
285
+ projectId: 'my-project',
286
+ labels: {
287
+ widgetButton: 'Report Issue',
288
+ feedbackTitle: 'Submit Feedback',
289
+ submitButton: 'Send',
290
+ cancelButton: 'Close',
291
+ },
292
+ }}
293
+ />
294
+ ```
295
+
296
+ ## Types
297
+
298
+ ```typescript
299
+ import type {
300
+ FeedbackItem,
301
+ FeedbackStatus,
302
+ FeedbackPriority,
303
+ Annotation,
304
+ AnnotationType,
305
+ UserInfo,
306
+ Comment,
307
+ MarkupConfig,
308
+ } from '@company/markup';
309
+ ```
310
+
311
+ ## Development
312
+
313
+ ```bash
314
+ # Install dependencies
315
+ npm install
316
+
317
+ # Build the package
318
+ npm run build
319
+
320
+ # Watch mode for development
321
+ npm run dev
322
+
323
+ # Type check
324
+ npm run type-check
325
+ ```
326
+
327
+ ## License
328
+
329
+ MIT
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { Annotation } from '../types';
3
+ interface AnnotationOverlayProps {
4
+ screenshot: string;
5
+ onComplete: (annotations: Annotation[], screenshot: string) => void;
6
+ onCancel: () => void;
7
+ }
8
+ export declare const AnnotationOverlay: React.FC<AnnotationOverlayProps>;
9
+ export default AnnotationOverlay;
@@ -0,0 +1,5 @@
1
+ interface AuthFormProps {
2
+ onSuccess: () => void;
3
+ }
4
+ export declare const AuthForm: ({ onSuccess }: AuthFormProps) => import("react/jsx-runtime").JSX.Element;
5
+ export {};
@@ -0,0 +1 @@
1
+ export declare const DebugPanel: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { FeedbackItem } from "../types";
3
+ interface FeedbackDetailProps {
4
+ feedback: FeedbackItem;
5
+ onBack: () => void;
6
+ onDelete?: (id: string) => void;
7
+ onUpdate?: (feedback: FeedbackItem) => void;
8
+ }
9
+ export declare const FeedbackDetail: React.FC<FeedbackDetailProps>;
10
+ export default FeedbackDetail;
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import type { FeedbackItem } from "../types";
3
+ interface FeedbackFormProps {
4
+ onSubmit: (feedback: FeedbackItem) => void;
5
+ onCancel: () => void;
6
+ }
7
+ export declare const FeedbackForm: React.FC<FeedbackFormProps>;
8
+ export default FeedbackForm;
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import type { FeedbackItem } from "../types";
3
+ interface FeedbackListProps {
4
+ feedbackItems: FeedbackItem[];
5
+ onSelectFeedback: (feedback: FeedbackItem) => void;
6
+ }
7
+ export declare const FeedbackList: React.FC<FeedbackListProps>;
8
+ export default FeedbackList;
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ interface IconProps extends React.SVGProps<SVGSVGElement> {
3
+ className?: string;
4
+ }
5
+ export declare const MessageIcon: React.FC<IconProps>;
6
+ export declare const CloseIcon: React.FC<IconProps>;
7
+ export declare const CameraIcon: React.FC<IconProps>;
8
+ export declare const ArrowIcon: React.FC<IconProps>;
9
+ export declare const RectangleIcon: React.FC<IconProps>;
10
+ export declare const CircleIcon: React.FC<IconProps>;
11
+ export declare const TextIcon: React.FC<IconProps>;
12
+ export declare const HighlightIcon: React.FC<IconProps>;
13
+ export declare const PencilIcon: React.FC<IconProps>;
14
+ export declare const UndoIcon: React.FC<IconProps>;
15
+ export declare const CheckIcon: React.FC<IconProps>;
16
+ export declare const BackIcon: React.FC<IconProps>;
17
+ export declare const TrashIcon: React.FC<IconProps>;
18
+ export declare const DownloadIcon: React.FC<IconProps>;
19
+ export declare const EditIcon: React.FC<IconProps>;
20
+ export declare const ClockIcon: React.FC<IconProps>;
21
+ export declare const LinkIcon: React.FC<IconProps>;
22
+ export declare const InboxIcon: React.FC<IconProps>;
23
+ export declare const SendIcon: React.FC<IconProps>;
24
+ export declare const SettingsIcon: React.FC<IconProps>;
25
+ export declare const PlusIcon: React.FC<IconProps>;
26
+ export declare const FilterIcon: React.FC<IconProps>;
27
+ export declare const ExternalLinkIcon: React.FC<IconProps>;
28
+ export declare const SquareIcon: React.FC<IconProps>;
29
+ export declare const ArrowRightIcon: React.FC<IconProps>;
30
+ export declare const TypeIcon: React.FC<IconProps>;
31
+ export declare const ArrowLeftIcon: React.FC<IconProps>;
32
+ export declare const UserIcon: React.FC<IconProps>;
33
+ export declare const ZoomInIcon: React.FC<IconProps>;
34
+ export declare const CalendarIcon: React.FC<IconProps>;
35
+ export declare const ChevronRightIcon: React.FC<IconProps>;
36
+ export {};
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import type { MarkupConfig } from "../types";
3
+ export interface MarkupWidgetProps {
4
+ config?: Partial<MarkupConfig>;
5
+ }
6
+ export declare const MarkupWidget: React.FC<MarkupWidgetProps>;
7
+ export default MarkupWidget;
@@ -0,0 +1,6 @@
1
+ export { MarkupWidget, default } from './MarkupWidget';
2
+ export { FeedbackForm } from './FeedbackForm';
3
+ export { FeedbackList } from './FeedbackList';
4
+ export { FeedbackDetail } from './FeedbackDetail';
5
+ export { AnnotationOverlay } from './AnnotationOverlay';
6
+ export { DebugPanel } from './DebugPanel';
@@ -0,0 +1 @@
1
+ export { useMarkup, default } from './useMarkup';
@@ -0,0 +1 @@
1
+ export declare const useFirebaseSync: () => void;
@@ -0,0 +1,21 @@
1
+ import type { FeedbackItem, MarkupConfig } from '../types';
2
+ interface UseMarkupOptions {
3
+ config: MarkupConfig;
4
+ syncWithServer?: boolean;
5
+ }
6
+ export declare function useMarkup({ config, syncWithServer }: UseMarkupOptions): {
7
+ isOpen: boolean;
8
+ feedbackItems: FeedbackItem[];
9
+ selectedFeedback: FeedbackItem | null;
10
+ openWidget: () => void;
11
+ closeWidget: () => void;
12
+ toggleWidget: () => void;
13
+ submitFeedback: (feedback: Omit<FeedbackItem, "id" | "createdAt" | "updatedAt">) => Promise<FeedbackItem>;
14
+ updateFeedback: (id: string, updates: Partial<FeedbackItem>) => Promise<void>;
15
+ deleteFeedback: (id: string) => Promise<void>;
16
+ selectFeedback: (feedback: FeedbackItem | null) => void;
17
+ openCount: number;
18
+ inProgressCount: number;
19
+ resolvedCount: number;
20
+ };
21
+ export default useMarkup;
@@ -0,0 +1,8 @@
1
+ export { MarkupWidget, default } from './components';
2
+ export { FeedbackForm, FeedbackList, FeedbackDetail, AnnotationOverlay } from './components';
3
+ export { useMarkup } from './hooks';
4
+ export { useMarkupStore } from './store';
5
+ export type { FeedbackStatus, FeedbackPriority, AnnotationType, Annotation, PageMetadata, FeedbackItem, UserInfo, Comment, MarkupConfig, MarkupState, MarkupActions, MarkupStore, } from './types';
6
+ export { captureScreenshot, getPageMetadata, getElementSelector, generateId, formatDate, getStatusColor, getPriorityColor, downloadScreenshot, copyToClipboard, MarkupAPI, } from './utils';
7
+ export { initFirebase } from './initFirebase';
8
+ export type { FirebaseConfig } from './initFirebase';