@eventop/sdk 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENCE ADDED
File without changes
package/README.md CHANGED
@@ -1,44 +1,315 @@
1
- # Sdk for event api
2
- ```javascript
3
- import { Eventop } from '@eventop/sdk';
4
-
5
- // Initialize client
6
- const eventop = new Eventop({
7
- apiKey: process.env.EVENTOP_API_KEY!, // sk_test_... or sk_live_...
8
- // environment auto-detected from key prefix
9
- });
1
+ # eventop/sdk
2
+
3
+ React integration for eventopAI. Features register at the **call site**, not
4
+ inside component definitions — so any generic component works with any feature,
5
+ and you never touch the component itself.
6
+
7
+ ---
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @eventop/sdk
13
+ ```
14
+
15
+ ---
16
+
17
+ ## The mental model
18
+
19
+ ```jsx
20
+ // ❌ Old way — registration inside the component, breaks reusability
21
+ function ExportButton() {
22
+ useeventopFeature({ id: 'export', name: 'Export' }); // hardcoded
23
+ return <button>Export</button>;
24
+ }
10
25
 
11
- // Create checkout session
12
- const session = await eventop.checkout.create({
13
- planId: 'premium-monthly',
14
- customerEmail: 'user@example.com',
15
- customerId: 'user_123',
16
- successUrl: 'https://yourdomain.com/success',
17
- cancelUrl: 'https://yourdomain.com/pricing',
18
- metadata: {
19
- userId: '123',
20
- source: 'web',
21
- },
26
+ // New way — registration at the call site, component stays generic
27
+ function Button({ children, onClick }) {
28
+ return <button onClick={onClick}>{children}</button>;
29
+ }
30
+
31
+ // Different features, same component, different places in the app
32
+ <eventopTarget id="export" name="Export Design" description="Download as PNG or SVG">
33
+ <Button onClick={handleExport}>Export</Button>
34
+ </eventopTarget>
35
+
36
+ <eventopTarget id="download-report" name="Download Report" description="Save as PDF">
37
+ <Button onClick={handleDownload}>Download</Button>
38
+ </eventopTarget>
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Quick start
44
+
45
+ ```jsx
46
+ // main.jsx — provider at the root, nothing else needed here
47
+ import { EventopAIProvider } from '@eventop/sdk';
48
+ import eventopAI from 'eventop-ai-sdk';
49
+
50
+ const provider = eventopAI.providers.custom(async ({ systemPrompt, messages }) => {
51
+ const res = await fetch('/api/guide', {
52
+ method: 'POST',
53
+ headers: { 'Content-Type': 'application/json' },
54
+ body: JSON.stringify({ systemPrompt, messages }),
55
+ });
56
+ return res.json();
22
57
  });
23
58
 
24
- console.log('Checkout URL:', session.url);
59
+ export default function App() {
60
+ return (
61
+ <eventopAIProvider
62
+ provider={provider}
63
+ appName="My App"
64
+ assistantName="AI Guide"
65
+ suggestions={['How do I export?', 'Invite a teammate']}
66
+ theme={{ mode: 'auto', tokens: { accent: '#6366f1' } }}
67
+ position={{ corner: 'bottom-right' }}
68
+ >
69
+ <YourApp />
70
+ </eventopAIProvider>
71
+ );
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Components
78
+
79
+ ### `<eventopTarget>`
80
+
81
+ Wraps any component and registers it as a feature. Works with your own components,
82
+ shadcn, MUI, Radix — anything. The wrapped component does not need to accept refs
83
+ or know about eventopAI.
84
+
85
+ ```jsx
86
+ import { EventopTarget } from '@eventop/sdk';
87
+
88
+ // Simple feature
89
+ <EventopTarget id="export" name="Export Design" description="Download as PNG or SVG">
90
+ <Button>Export</Button>
91
+ </EventopTarget>
92
+
93
+ // With navigation — for features behind a route change
94
+ <EventopTarget
95
+ id="effects"
96
+ name="Effects Panel"
97
+ description="Apply shadows, blur and glow"
98
+ navigate={() => router.push('/canvas')}
99
+ navigateWaitFor="#canvas-stage"
100
+ >
101
+ <EffectsPanel />
102
+ </EventopTarget>
103
+
104
+ // Auto-advance the tour when clicked
105
+ <EventopTarget
106
+ id="open-settings"
107
+ name="Settings"
108
+ description="Open the workspace settings"
109
+ advanceOn={{ event: 'click', delay: 200 }}
110
+ >
111
+ <SettingsButton />
112
+ </EventopTarget>
113
+ ```
114
+
115
+ **Props:**
116
+
117
+ | Prop | Type | Required | Description |
118
+ |-------------------|----------|----------|-------------------------------------------------------|
119
+ | `id` | string | ✓ | Unique feature id |
120
+ | `name` | string | ✓ | Human-readable name (AI reads this) |
121
+ | `description` | string | | What it does (AI uses this to match user intent) |
122
+ | `navigate` | function | | Navigate here if component is not currently mounted |
123
+ | `navigateWaitFor` | string | | CSS selector to wait for after navigating |
124
+ | `advanceOn` | object | | `{ event, delay?, selector? }` — auto-advance the tour|
125
+ | `waitFor` | string | | CSS selector to wait for before showing this step |
126
+
127
+ ---
128
+
129
+ ### `<eventopStep>`
130
+
131
+ Registers one step in a multi-step flow. Can live **anywhere** in the component
132
+ tree — it does not need to be inside a `eventopTarget`. Steps self-assemble
133
+ into the correct sequence via the `index` prop.
134
+
135
+ ```jsx
136
+ import { EventopStep } from '@eventop/sdk';
137
+
138
+ // These three components are totally separate — different files, different parents
139
+ // They form a flow by sharing the same feature id and sequential indexes
140
+
141
+ // In CanvasStage.jsx
142
+ <EventopStep
143
+ feature="drop-shadow"
144
+ index={0}
145
+ advanceOn={{ selector: '.canvas-el', event: 'click', delay: 300 }}
146
+ >
147
+ <div className="canvas-stage">...</div>
148
+ </EventopStep>
149
+
150
+ // In Toolbar.jsx
151
+ <EventopStep
152
+ feature="drop-shadow"
153
+ index={1}
154
+ waitFor=".canvas-el.selected"
155
+ advanceOn={{ event: 'click', delay: 200 }}
156
+ >
157
+ <button id="btn-effects">Effects</button>
158
+ </EventopStep>
25
159
 
26
- // Verify webhook
27
- app.post('/webhooks/eventop', express.raw({ type: 'application/json' }), (req, res) => {
28
- const signature = req.headers['x-webhook-signature'] as string;
29
- const payload = req.body.toString();
160
+ // In EffectsPanel.jsx
161
+ <EventopStep
162
+ feature="drop-shadow"
163
+ index={2}
164
+ waitFor="#effects-panel.open"
165
+ advanceOn={{ event: 'click', delay: 300 }}
166
+ >
167
+ <button id="shadow-toggle">Shadow</button>
168
+ </EventopStep>
30
169
 
31
- try {
32
- const event = eventop.webhooks.constructEvent(
33
- payload,
34
- signature,
35
- process.env.WEBHOOK_SECRET!,
36
- );
170
+ // Only renders when shadow is on — SDK waits for it via waitFor
171
+ <EventopStep feature="drop-shadow" index={3} waitFor="#shadow-controls.visible">
172
+ <div id="shadow-controls">...</div>
173
+ </EventopStep>
174
+ ```
37
175
 
38
- console.log('Received event:', event.event);
39
- res.json({ received: true });
40
- } catch (err) {
41
- res.status(400).send('Webhook signature verification failed');
176
+ **Nested sub-steps:**
177
+
178
+ Pass `parentStep` to model steps that belong inside another step.
179
+ Useful for things like "open font picker → (select font family → select weight → set size)".
180
+
181
+ ```jsx
182
+ // Step 1: open font picker
183
+ <EventopStep feature="style-text" index={1}>
184
+ <FontPickerButton />
185
+ </EventopStep>
186
+
187
+ // Sub-steps of step 1 — run after step 1, in order
188
+ <EventopStep feature="style-text" index={0} parentStep={1} waitFor=".font-picker.open">
189
+ <FontFamilyList />
190
+ </EventopStep>
191
+ <EventopStep feature="style-text" index={1} parentStep={1} waitFor=".family-selected">
192
+ <FontWeightList />
193
+ </EventopStep>
194
+ ```
195
+
196
+ **Implicit feature id from `eventopTarget` ancestor:**
197
+
198
+ If `eventopStep` is inside a `eventopTarget`, it inherits the feature id:
199
+
200
+ ```jsx
201
+ <EventopTarget id="drop-shadow" name="Drop Shadow" description="...">
202
+ <div>
203
+ <EventopStep index={0} advanceOn={{ selector: '.el', event: 'click' }}>
204
+ <Canvas />
205
+ </EventopStep>
206
+ <EventopStep index={1} waitFor=".el.selected">
207
+ <EffectsButton />
208
+ </EventopStep>
209
+ </div>
210
+ </EventopTarget>
211
+ ```
212
+
213
+ **Props:**
214
+
215
+ | Prop | Type | Required | Description |
216
+ |--------------|--------|----------|----------------------------------------------------------|
217
+ | `feature` | string | * | Feature id this step belongs to (*not needed if inside `eventopTarget`) |
218
+ | `index` | number | ✓ | Position in the flow (0-based) |
219
+ | `parentStep` | number | | Parent step index — makes this a sub-step |
220
+ | `waitFor` | string | | CSS selector to wait for before showing |
221
+ | `advanceOn` | object | | `{ event, delay?, selector? }` — auto-advance |
222
+
223
+ ---
224
+
225
+ ### `useeventopAI`
226
+
227
+ Call SDK methods from inside any component. Use for `stepComplete()` and
228
+ `stepFail()` when you have async validation the tour should respect.
229
+
230
+ ```jsx
231
+ import { useeventopAI } from '@eventop/sdk';
232
+
233
+ function CheckoutStep() {
234
+ const { stepComplete, stepFail } = useeventopAI();
235
+
236
+ async function handleContinue() {
237
+ const ok = await validateCard(cardNumber);
238
+ if (ok) stepComplete(); // tour advances to next step
239
+ else stepFail('Invalid card number — please try again.');
42
240
  }
43
- });
44
- ```
241
+
242
+ return <button onClick={handleContinue}>Continue</button>;
243
+ }
244
+ ```
245
+
246
+ **Returns:**
247
+
248
+ | Method | Description |
249
+ |------------------|-----------------------------------------------------|
250
+ | `stepComplete()` | Advance the active tour step |
251
+ | `stepFail(msg)` | Block advancement and show error in the tooltip |
252
+ | `open()` | Open the chat panel |
253
+ | `close()` | Close the chat panel |
254
+ | `cancelTour()` | Hard cancel — no resume state saved |
255
+ | `resumeTour()` | Resume a paused tour from where it left off |
256
+ | `isActive()` | Returns true if a tour is running |
257
+ | `isPaused()` | Returns true if a tour is paused |
258
+ | `runTour(steps)` | Run a manual tour bypassing the AI |
259
+
260
+ ---
261
+
262
+ ### `useeventopTour`
263
+
264
+ React to tour state in your own UI. Reactive — updates every 300ms.
265
+
266
+ ```jsx
267
+ import { useeventopTour } from '@eventop/sdk';
268
+
269
+ function TourStatusBar() {
270
+ const { isActive, isPaused, resume, cancel } = useeventopTour();
271
+
272
+ if (!isActive && !isPaused) return null;
273
+
274
+ return (
275
+ <div className="tour-bar">
276
+ {isPaused ? (
277
+ <>
278
+ <span>⏸ Tour paused</span>
279
+ <button onClick={resume}>Resume</button>
280
+ </>
281
+ ) : (
282
+ <span>▶ Guided tour running</span>
283
+ )}
284
+ <button onClick={cancel}>End tour</button>
285
+ </div>
286
+ );
287
+ }
288
+ ```
289
+
290
+ ---
291
+
292
+ ## Screen navigation
293
+
294
+ Because `eventopTarget` only registers a feature while its component is mounted,
295
+ screen detection is automatic. If the Effects Panel is not rendered, the `effects`
296
+ feature simply does not exist in the registry — no `screen.check()` needed.
297
+
298
+ For features that live behind a route change, pass `navigate`:
299
+
300
+ ```jsx
301
+ // Only rendered on /canvas route
302
+ function EffectsPanel() {
303
+ return (
304
+ <EventopTarget
305
+ id="effects"
306
+ name="Effects Panel"
307
+ description="Apply shadows and blur"
308
+ navigate={() => router.push('/canvas')}
309
+ navigateWaitFor="#canvas-root"
310
+ >
311
+ <div id="effects-panel">...</div>
312
+ </EventopTarget>
313
+ );
314
+ }
315
+ ```
@@ -1,52 +1,54 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.EventopClient = void 0;
7
- const node_fetch_1 = __importDefault(require("node-fetch"));
8
- const errors_1 = require("./errors");
9
- class EventopClient {
10
- constructor(config) {
11
- this.apiKey = config.apiKey;
12
- // Set base URL based on environment
13
- if (config.apiUrl) {
14
- this.baseUrl = config.apiUrl;
15
- }
16
- else {
17
- const env = config.environment || this.detectEnvironment();
18
- this.baseUrl =
19
- env === 'devnet'
20
- ? 'https://eventop-server-app-production.up.railway.app'
21
- : 'https://api.eventop.xyz';
22
- }
23
- }
24
- detectEnvironment() {
25
- // Detect from API key prefix
26
- if (this.apiKey.startsWith('sk_test_')) {
27
- return 'devnet';
28
- }
29
- else if (this.apiKey.startsWith('sk_live_')) {
30
- return 'mainnet';
31
- }
32
- throw new errors_1.AuthenticationError('Invalid API key format');
33
- }
34
- async request(method, path, body) {
35
- const url = `${this.baseUrl}${path}`;
36
- const headers = {
37
- 'Authorization': `Bearer ${this.apiKey}`,
38
- 'Content-Type': 'application/json',
39
- };
40
- const response = await (0, node_fetch_1.default)(url, {
41
- method,
42
- headers,
43
- body: body ? JSON.stringify(body) : undefined,
44
- });
45
- const data = await response.json();
46
- if (!response.ok) {
47
- throw new errors_1.EventopError(data.message || 'Request failed', response.status, data.code);
48
- }
49
- return data;
50
- }
51
- }
52
- exports.EventopClient = EventopClient;
1
+ // import fetch from 'node-fetch';
2
+ // import { EventopConfig, Environment } from './types';
3
+ // import { EventopError, AuthenticationError } from './errors';
4
+ // export class EventopClient {
5
+ // private apiKey: string;
6
+ // private baseUrl: string;
7
+ // constructor(config: EventopConfig) {
8
+ // this.apiKey = config.apiKey;
9
+ // // Set base URL based on environment
10
+ // if (config.apiUrl) {
11
+ // this.baseUrl = config.apiUrl;
12
+ // } else {
13
+ // const env = config.environment || this.detectEnvironment();
14
+ // this.baseUrl =
15
+ // env === 'devnet'
16
+ // ? 'https://eventop-server-app-production.up.railway.app'
17
+ // : 'https://api.eventop.xyz';
18
+ // }
19
+ // }
20
+ // private detectEnvironment(): Environment {
21
+ // // Detect from API key prefix
22
+ // if (this.apiKey.startsWith('sk_test_')) {
23
+ // return 'devnet';
24
+ // } else if (this.apiKey.startsWith('sk_live_')) {
25
+ // return 'mainnet';
26
+ // }
27
+ // throw new AuthenticationError('Invalid API key format');
28
+ // }
29
+ // async request<T>(
30
+ // method: string,
31
+ // path: string,
32
+ // body?: any,
33
+ // ): Promise<T> {
34
+ // const url = `${this.baseUrl}${path}`;
35
+ // const headers: Record<string, string> = {
36
+ // 'Authorization': `Bearer ${this.apiKey}`,
37
+ // 'Content-Type': 'application/json',
38
+ // };
39
+ // const response = await fetch(url, {
40
+ // method,
41
+ // headers,
42
+ // body: body ? JSON.stringify(body) : undefined,
43
+ // });
44
+ // const data = await response.json();
45
+ // if (!response.ok) {
46
+ // throw new EventopError(
47
+ // data.message || 'Request failed',
48
+ // response.status,
49
+ // data.code,
50
+ // );
51
+ // }
52
+ // return data as T;
53
+ // }
54
+ // }
@@ -1,33 +1,28 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NotFoundError = exports.InvalidRequestError = exports.AuthenticationError = exports.EventopError = void 0;
4
- class EventopError extends Error {
5
- constructor(message, statusCode, code) {
6
- super(message);
7
- this.statusCode = statusCode;
8
- this.code = code;
9
- this.name = 'EventopError';
10
- }
11
- }
12
- exports.EventopError = EventopError;
13
- class AuthenticationError extends EventopError {
14
- constructor(message = 'Invalid API key') {
15
- super(message, 401, 'authentication_error');
16
- this.name = 'AuthenticationError';
17
- }
18
- }
19
- exports.AuthenticationError = AuthenticationError;
20
- class InvalidRequestError extends EventopError {
21
- constructor(message) {
22
- super(message, 400, 'invalid_request');
23
- this.name = 'InvalidRequestError';
24
- }
25
- }
26
- exports.InvalidRequestError = InvalidRequestError;
27
- class NotFoundError extends EventopError {
28
- constructor(message) {
29
- super(message, 404, 'not_found');
30
- this.name = 'NotFoundError';
31
- }
32
- }
33
- exports.NotFoundError = NotFoundError;
1
+ // export class EventopError extends Error {
2
+ // constructor(
3
+ // message: string,
4
+ // public statusCode?: number,
5
+ // public code?: string,
6
+ // ) {
7
+ // super(message);
8
+ // this.name = 'EventopError';
9
+ // }
10
+ // }
11
+ // export class AuthenticationError extends EventopError {
12
+ // constructor(message: string = 'Invalid API key') {
13
+ // super(message, 401, 'authentication_error');
14
+ // this.name = 'AuthenticationError';
15
+ // }
16
+ // }
17
+ // export class InvalidRequestError extends EventopError {
18
+ // constructor(message: string) {
19
+ // super(message, 400, 'invalid_request');
20
+ // this.name = 'InvalidRequestError';
21
+ // }
22
+ // }
23
+ // export class NotFoundError extends EventopError {
24
+ // constructor(message: string) {
25
+ // super(message, 404, 'not_found');
26
+ // this.name = 'NotFoundError';
27
+ // }
28
+ // }