@bitovi/vybit 0.8.2 → 0.9.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/shared/types.ts CHANGED
@@ -1,9 +1,34 @@
1
1
  // Shared types for the PATCH protocol.
2
2
  // Imported by overlay (esbuild), panel (Vite), and server (tsx).
3
3
 
4
+ /** Cached ghost HTML + host styles for instant component preview placeholders. */
5
+ export interface GhostCacheEntry {
6
+ storyId: string;
7
+ argsHash: string;
8
+ ghostHtml: string;
9
+ hostStyles: Record<string, string>;
10
+ storyBackground?: string;
11
+ componentName: string;
12
+ componentPath?: string;
13
+ extractedAt: number;
14
+ }
15
+
4
16
  export type ContainerName = 'modal' | 'popover' | 'sidebar' | 'popup';
5
17
 
6
- export type PatchKind = 'class-change' | 'message' | 'design';
18
+ /** A component placed on the Fabric.js design canvas */
19
+ export interface CanvasComponent {
20
+ componentName: string;
21
+ componentPath?: string; // e.g. './src/components/Button.tsx'
22
+ storyId?: string;
23
+ args?: Record<string, unknown>;
24
+ // Position/size on the canvas (px, relative to canvas top-left)
25
+ x: number;
26
+ y: number;
27
+ width: number;
28
+ height: number;
29
+ }
30
+
31
+ export type PatchKind = 'class-change' | 'message' | 'design' | 'component-drop';
7
32
 
8
33
  export type PatchStatus = 'staged' | 'committed' | 'implementing' | 'implemented' | 'error';
9
34
 
@@ -30,6 +55,15 @@ export interface Patch {
30
55
  insertMode?: string; // before | after | first-child | last-child
31
56
  canvasWidth?: number;
32
57
  canvasHeight?: number;
58
+ canvasComponents?: CanvasComponent[]; // Components placed on the design canvas
59
+ // Component-drop fields (used when kind === 'component-drop'):
60
+ ghostHtml?: string; // HTML of the dropped component (overlay preview only — stripped from MCP response)
61
+ componentStoryId?: string; // Storybook story ID
62
+ componentPath?: string; // Source file of the component, e.g. './src/components/Button.tsx'
63
+ componentArgs?: Record<string, unknown>; // Props the user configured before dropping
64
+ parentComponent?: { name: string }; // React component that contains the drop target
65
+ targetPatchId?: string; // If target is a ghost from an earlier drop, references that patch
66
+ targetComponentName?: string; // Name of the ghost component being referenced
33
67
  // Commit reference:
34
68
  commitId?: string; // Set when committed into a Commit
35
69
  }
@@ -57,6 +91,12 @@ export interface PatchSummary {
57
91
  errorMessage?: string;
58
92
  message?: string;
59
93
  image?: string;
94
+ canvasComponents?: CanvasComponent[];
95
+ // Component-drop display fields:
96
+ insertMode?: string;
97
+ parentComponent?: { name: string };
98
+ targetComponentName?: string;
99
+ targetPatchId?: string;
60
100
  }
61
101
 
62
102
  export interface CommitSummary {
@@ -270,6 +310,7 @@ export interface DesignSubmitMessage {
270
310
  insertMode: InsertMode;
271
311
  canvasWidth: number;
272
312
  canvasHeight: number;
313
+ canvasComponents?: CanvasComponent[];
273
314
  }
274
315
 
275
316
  /** Design iframe → Overlay: close the canvas wrapper */
@@ -282,6 +323,44 @@ export interface ClosePanelMessage {
282
323
  type: 'CLOSE_PANEL';
283
324
  }
284
325
 
326
+ /** Overlay → Server: story changed in Storybook, clear panel selection */
327
+ export interface ResetSelectionMessage {
328
+ type: 'RESET_SELECTION';
329
+ }
330
+
331
+ // ---------------------------------------------------------------------------
332
+ // Component arm-and-place messages
333
+ // ---------------------------------------------------------------------------
334
+
335
+ /** Panel → Overlay: user armed a component for placement from the Draw tab */
336
+ export interface ComponentArmMessage {
337
+ type: 'COMPONENT_ARM';
338
+ to: 'overlay';
339
+ componentName: string;
340
+ storyId: string;
341
+ ghostHtml: string;
342
+ componentPath?: string; // Source file path from Storybook index, e.g. './src/components/Button.tsx'
343
+ args?: Record<string, unknown>; // Current prop values from ArgsForm
344
+ }
345
+
346
+ /** Panel → Overlay: user cancelled the armed state (panel click or escape) */
347
+ export interface ComponentDisarmMessage {
348
+ type: 'COMPONENT_DISARM';
349
+ to: 'overlay';
350
+ }
351
+
352
+ /** Overlay → Panel: overlay has disarmed (user placed or pressed Escape in app) */
353
+ export interface ComponentDisarmedMessage {
354
+ type: 'COMPONENT_DISARMED';
355
+ to: 'panel';
356
+ }
357
+
358
+ /** Overlay → Server: component was placed, stage a patch */
359
+ export interface ComponentDroppedMessage {
360
+ type: 'COMPONENT_DROPPED';
361
+ patch: Patch;
362
+ }
363
+
285
364
  // ---------------------------------------------------------------------------
286
365
  // Union types
287
366
  // ---------------------------------------------------------------------------
@@ -296,8 +375,10 @@ export type PanelToOverlay =
296
375
  | SwitchContainerMessage
297
376
  | InsertDesignCanvasMessage
298
377
  | CaptureScreenshotMessage
299
- | ClosePanelMessage;
300
- export type OverlayToServer = PatchStagedMessage;
378
+ | ClosePanelMessage
379
+ | ComponentArmMessage
380
+ | ComponentDisarmMessage;
381
+ export type OverlayToServer = PatchStagedMessage | ComponentDroppedMessage | ResetSelectionMessage;
301
382
  export type PanelToServer = PatchCommitMessage | MessageStageMessage;
302
383
  export type ClientToServer =
303
384
  | RegisterMessage
@@ -306,10 +387,13 @@ export type ClientToServer =
306
387
  | MessageStageMessage
307
388
  | DesignSubmitMessage
308
389
  | DesignCloseMessage
390
+ | ComponentDroppedMessage
391
+ | ResetSelectionMessage
309
392
  | PingMessage;
310
393
  export type ServerToClient =
311
394
  | PongMessage
312
395
  | QueueUpdateMessage
396
+ | ResetSelectionMessage
313
397
  | PatchUpdateMessage
314
398
  | PatchImplementingMessage
315
399
  | PatchImplementedMessage
@@ -336,5 +420,10 @@ export type AnyMessage =
336
420
  | DesignSubmitMessage
337
421
  | DesignCloseMessage
338
422
  | ClosePanelMessage
423
+ | ComponentArmMessage
424
+ | ComponentDisarmMessage
425
+ | ComponentDisarmedMessage
426
+ | ComponentDroppedMessage
427
+ | ResetSelectionMessage
339
428
  | PingMessage
340
429
  | PongMessage;
@@ -0,0 +1 @@
1
+ module.exports = require('./preset');
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { addons, types } from '@storybook/manager-api';
3
+ import { AddonPanel } from '@storybook/components';
4
+
5
+ const ADDON_ID = 'vybit';
6
+ const PANEL_ID = `${ADDON_ID}/panel`;
7
+
8
+ addons.register(ADDON_ID, (api) => {
9
+ addons.add(PANEL_ID, {
10
+ type: types.PANEL,
11
+ title: 'Vybit',
12
+ paramKey: 'vybit',
13
+ render: ({ active }) => {
14
+ const serverUrl =
15
+ api.getCurrentParameter<{ serverUrl?: string }>('vybit')?.serverUrl
16
+ ?? 'http://localhost:3333';
17
+
18
+ if (active) {
19
+ api.togglePanelPosition('right');
20
+ }
21
+
22
+ return (
23
+ <AddonPanel active={active ?? false}>
24
+ <iframe
25
+ src={`${serverUrl}/panel/`}
26
+ style={{ width: '100%', height: '100%', border: 'none' }}
27
+ title="Vybit Panel"
28
+ />
29
+ </AddonPanel>
30
+ );
31
+ },
32
+ });
33
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@bitovi/vybit-storybook-addon",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "description": "Storybook addon wiring for Vybit — overlay injection + panel registration",
7
+ "peerDependencies": {
8
+ "storybook": "^8.0.0"
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ const { join } = require('path');
2
+
3
+ module.exports = {
4
+ previewAnnotations(entry = []) {
5
+ return [...entry, join(__dirname, './preview.ts')];
6
+ },
7
+ managerEntries(entry = []) {
8
+ return [...entry, join(__dirname, './manager.tsx')];
9
+ },
10
+ };
@@ -0,0 +1,29 @@
1
+ import { addons } from '@storybook/preview-api';
2
+
3
+ let injected = false;
4
+
5
+ export const decorators = [
6
+ (StoryFn: any, context: any) => {
7
+ const serverUrl =
8
+ context.parameters?.vybit?.serverUrl ?? 'http://localhost:3333';
9
+
10
+ if (!injected) {
11
+ const script = document.createElement('script');
12
+ script.src = `${serverUrl}/overlay.js`;
13
+ document.head.appendChild(script);
14
+ injected = true;
15
+ }
16
+
17
+ return StoryFn();
18
+ },
19
+ ];
20
+
21
+ const channel = addons.getChannel();
22
+ let lastStoryId: string | undefined;
23
+
24
+ channel.on('storyRendered', (storyId?: string) => {
25
+ // Only reset selection on actual story navigation, not HMR updates
26
+ if (storyId && storyId === lastStoryId) return;
27
+ lastStoryId = storyId;
28
+ window.postMessage({ type: 'STORYBOOK_STORY_RENDERED' }, '*');
29
+ });