@bitovi/vybit 0.11.7 → 0.13.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 +3 -2
- package/overlay/dist/overlay.js +5985 -2572
- package/package.json +5 -1
- package/panel/dist/assets/{DesignMode-Yfq3lye8.js → DesignMode-DotlKICs.js} +15 -15
- package/panel/dist/assets/index-BWb2WAKo.js +61 -0
- package/panel/dist/assets/index-DN-H7d0q.css +1 -0
- package/panel/dist/index.html +2 -2
- package/server/app.ts +2 -0
- package/server/mcp-tools.ts +115 -4
- package/server/queue.ts +46 -0
- package/server/storybook.ts +4 -1
- package/server/tailwind-v4.ts +10 -5
- package/server/websocket.ts +20 -1
- package/shared/types.ts +251 -3
- package/storybook-addon/preview-v10.ts +17 -12
- package/storybook-addon/preview.ts +17 -12
- package/panel/dist/assets/index-0eiAXBf9.js +0 -62
- package/panel/dist/assets/index-BWp0wXqx.css +0 -1
package/shared/types.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface CanvasComponent {
|
|
|
28
28
|
height: number;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export type PatchKind = 'class-change' | 'message' | 'design' | 'component-drop';
|
|
31
|
+
export type PatchKind = 'class-change' | 'message' | 'design' | 'component-drop' | 'text-change' | 'bug-report';
|
|
32
32
|
|
|
33
33
|
export type PatchStatus = 'staged' | 'committed' | 'implementing' | 'implemented' | 'error';
|
|
34
34
|
|
|
@@ -60,10 +60,19 @@ export interface Patch {
|
|
|
60
60
|
ghostHtml?: string; // HTML of the dropped component (overlay preview only — stripped from MCP response)
|
|
61
61
|
componentStoryId?: string; // Storybook story ID
|
|
62
62
|
componentPath?: string; // Source file of the component, e.g. './src/components/Button.tsx'
|
|
63
|
+
// Text-change fields (used when kind === 'text-change'):
|
|
64
|
+
originalHtml?: string; // HTML before text edit
|
|
65
|
+
newHtml?: string; // HTML after text edit
|
|
63
66
|
componentArgs?: Record<string, unknown>; // Props the user configured before dropping
|
|
64
67
|
parentComponent?: { name: string }; // React component that contains the drop target
|
|
65
68
|
targetPatchId?: string; // If target is a ghost from an earlier drop, references that patch
|
|
66
69
|
targetComponentName?: string; // Name of the ghost component being referenced
|
|
70
|
+
// Bug-report fields (used when kind === 'bug-report'):
|
|
71
|
+
bugDescription?: string;
|
|
72
|
+
bugScreenshots?: string[];
|
|
73
|
+
bugTimeline?: BugTimelineEntry[];
|
|
74
|
+
bugTimeRange?: { start: string; end: string };
|
|
75
|
+
bugElement?: BugReportElement | null;
|
|
67
76
|
// Commit reference:
|
|
68
77
|
commitId?: string; // Set when committed into a Commit
|
|
69
78
|
}
|
|
@@ -97,6 +106,11 @@ export interface PatchSummary {
|
|
|
97
106
|
parentComponent?: { name: string };
|
|
98
107
|
targetComponentName?: string;
|
|
99
108
|
targetPatchId?: string;
|
|
109
|
+
// Text-change display fields:
|
|
110
|
+
originalHtml?: string;
|
|
111
|
+
newHtml?: string;
|
|
112
|
+
// Bug-report display fields:
|
|
113
|
+
bugDescription?: string;
|
|
100
114
|
}
|
|
101
115
|
|
|
102
116
|
export interface CommitSummary {
|
|
@@ -343,6 +357,7 @@ export interface ComponentArmMessage {
|
|
|
343
357
|
ghostHtml: string;
|
|
344
358
|
componentPath?: string; // Source file path from Storybook index, e.g. './src/components/Button.tsx'
|
|
345
359
|
args?: Record<string, unknown>; // Current prop values from ArgsForm
|
|
360
|
+
insertMode?: 'replace'; // When 'replace', arms element-select if no element is selected
|
|
346
361
|
}
|
|
347
362
|
|
|
348
363
|
/** Panel → Overlay: user cancelled the armed state (panel click or escape) */
|
|
@@ -357,6 +372,41 @@ export interface ComponentDisarmedMessage {
|
|
|
357
372
|
to: 'panel';
|
|
358
373
|
}
|
|
359
374
|
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
// Mode sync messages
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
|
|
379
|
+
export type AppMode = 'select' | 'insert' | 'bug-report' | null;
|
|
380
|
+
export type SelectTab = 'design' | 'replace';
|
|
381
|
+
export type InsertTab = 'place';
|
|
382
|
+
export type PanelTab = SelectTab | InsertTab;
|
|
383
|
+
|
|
384
|
+
/** Bidirectional: panel ↔ overlay mode change */
|
|
385
|
+
export interface ModeChangedMessage {
|
|
386
|
+
type: 'MODE_CHANGED';
|
|
387
|
+
to: 'overlay' | 'panel';
|
|
388
|
+
mode: AppMode;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Bidirectional: panel ↔ overlay tab change */
|
|
392
|
+
export interface TabChangedMessage {
|
|
393
|
+
type: 'TAB_CHANGED';
|
|
394
|
+
to: 'overlay' | 'panel';
|
|
395
|
+
tab: PanelTab;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Overlay → Panel: text editing started on an element */
|
|
399
|
+
export interface TextEditActiveMessage {
|
|
400
|
+
type: 'TEXT_EDIT_ACTIVE';
|
|
401
|
+
to: 'panel';
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** Overlay → Panel: text editing ended */
|
|
405
|
+
export interface TextEditDoneMessage {
|
|
406
|
+
type: 'TEXT_EDIT_DONE';
|
|
407
|
+
to: 'panel';
|
|
408
|
+
}
|
|
409
|
+
|
|
360
410
|
/** Overlay → Server: component was placed, stage a patch */
|
|
361
411
|
export interface ComponentDroppedMessage {
|
|
362
412
|
type: 'COMPONENT_DROPPED';
|
|
@@ -379,7 +429,9 @@ export type PanelToOverlay =
|
|
|
379
429
|
| CaptureScreenshotMessage
|
|
380
430
|
| ClosePanelMessage
|
|
381
431
|
| ComponentArmMessage
|
|
382
|
-
| ComponentDisarmMessage
|
|
432
|
+
| ComponentDisarmMessage
|
|
433
|
+
| ModeChangedMessage
|
|
434
|
+
| TabChangedMessage;
|
|
383
435
|
export type OverlayToServer = PatchStagedMessage | ComponentDroppedMessage | ResetSelectionMessage;
|
|
384
436
|
export type PanelToServer = PatchCommitMessage | MessageStageMessage;
|
|
385
437
|
export type ClientToServer =
|
|
@@ -427,5 +479,201 @@ export type AnyMessage =
|
|
|
427
479
|
| ComponentDisarmedMessage
|
|
428
480
|
| ComponentDroppedMessage
|
|
429
481
|
| ResetSelectionMessage
|
|
482
|
+
| ModeChangedMessage
|
|
483
|
+
| TabChangedMessage
|
|
484
|
+
| TextEditActiveMessage
|
|
485
|
+
| TextEditDoneMessage
|
|
430
486
|
| PingMessage
|
|
431
|
-
| PongMessage
|
|
487
|
+
| PongMessage
|
|
488
|
+
| RecordingGetHistoryMessage
|
|
489
|
+
| RecordingHistoryMessage
|
|
490
|
+
| RecordingGetSnapshotMessage
|
|
491
|
+
| RecordingSnapshotMessage
|
|
492
|
+
| RecordingGetRangeMessage
|
|
493
|
+
| RecordingRangeMessage
|
|
494
|
+
| RecordingSnapshotMetaMessage
|
|
495
|
+
| BugReportPickElementMessage
|
|
496
|
+
| BugReportElementPickedMessage
|
|
497
|
+
| BugReportPickCancelledMessage
|
|
498
|
+
| BugReportStageMessage;
|
|
499
|
+
|
|
500
|
+
// ---------------------------------------------------------------------------
|
|
501
|
+
// Recording / Bug Report types
|
|
502
|
+
// ---------------------------------------------------------------------------
|
|
503
|
+
|
|
504
|
+
export interface ConsoleEntry {
|
|
505
|
+
level: 'log' | 'warn' | 'error' | 'info';
|
|
506
|
+
args: string[];
|
|
507
|
+
timestamp: string;
|
|
508
|
+
stack?: string;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export interface NetworkError {
|
|
512
|
+
url: string;
|
|
513
|
+
method: string;
|
|
514
|
+
status?: number;
|
|
515
|
+
statusText?: string;
|
|
516
|
+
errorMessage?: string;
|
|
517
|
+
timestamp: string;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export interface BugReportElement {
|
|
521
|
+
tag: string;
|
|
522
|
+
id?: string;
|
|
523
|
+
classes: string;
|
|
524
|
+
selectorPath: string;
|
|
525
|
+
componentName?: string;
|
|
526
|
+
outerHTML: string;
|
|
527
|
+
boundingBox: { x: number; y: number; width: number; height: number };
|
|
528
|
+
screenshot?: string;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export type SnapshotTrigger = 'mutation' | 'click' | 'error' | 'navigation' | 'page-load';
|
|
532
|
+
|
|
533
|
+
export interface NavigationInfo {
|
|
534
|
+
from: string;
|
|
535
|
+
to: string | null;
|
|
536
|
+
method: 'pushState' | 'replaceState' | 'popstate' | 'full-page';
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** A structured description of a single DOM mutation */
|
|
540
|
+
export interface DomChange {
|
|
541
|
+
type: 'attribute' | 'text' | 'childList';
|
|
542
|
+
selector: string;
|
|
543
|
+
componentName?: string;
|
|
544
|
+
/** attribute changes */
|
|
545
|
+
attributeName?: string;
|
|
546
|
+
oldValue?: string;
|
|
547
|
+
newValue?: string;
|
|
548
|
+
/** text changes */
|
|
549
|
+
oldText?: string;
|
|
550
|
+
newText?: string;
|
|
551
|
+
/** childList changes */
|
|
552
|
+
addedCount?: number;
|
|
553
|
+
removedCount?: number;
|
|
554
|
+
addedHTML?: string;
|
|
555
|
+
removedHTML?: string;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/** A single chronological event in a bug report timeline */
|
|
559
|
+
export interface BugTimelineEntry {
|
|
560
|
+
timestamp: string;
|
|
561
|
+
trigger: SnapshotTrigger;
|
|
562
|
+
url: string;
|
|
563
|
+
consoleLogs?: ConsoleEntry[];
|
|
564
|
+
networkErrors?: NetworkError[];
|
|
565
|
+
domChanges?: DomChange[];
|
|
566
|
+
domSnapshot?: string;
|
|
567
|
+
domDiff?: string;
|
|
568
|
+
hasScreenshot?: boolean;
|
|
569
|
+
elementInfo?: { tag: string; classes: string; id?: string; innerText?: string; componentName?: string };
|
|
570
|
+
navigationInfo?: NavigationInfo;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export interface RecordingSnapshot {
|
|
574
|
+
id?: number;
|
|
575
|
+
timestamp: string;
|
|
576
|
+
trigger: SnapshotTrigger;
|
|
577
|
+
isKeyframe: boolean;
|
|
578
|
+
domSnapshot?: string;
|
|
579
|
+
domDiff?: string;
|
|
580
|
+
domChanges?: DomChange[];
|
|
581
|
+
screenshot?: string;
|
|
582
|
+
thumbnail?: string;
|
|
583
|
+
consoleLogs: ConsoleEntry[];
|
|
584
|
+
networkErrors: NetworkError[];
|
|
585
|
+
url: string;
|
|
586
|
+
scrollPosition: { x: number; y: number };
|
|
587
|
+
viewportSize: { width: number; height: number };
|
|
588
|
+
elementInfo?: { tag: string; classes: string; id?: string; innerText?: string; componentName?: string };
|
|
589
|
+
navigationInfo?: NavigationInfo;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
export interface SnapshotMeta {
|
|
593
|
+
id: number;
|
|
594
|
+
timestamp: string;
|
|
595
|
+
trigger: SnapshotTrigger;
|
|
596
|
+
isKeyframe: boolean;
|
|
597
|
+
thumbnail?: string;
|
|
598
|
+
elementInfo?: RecordingSnapshot['elementInfo'];
|
|
599
|
+
consoleErrorCount: number;
|
|
600
|
+
networkErrorCount: number;
|
|
601
|
+
url: string;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ---------------------------------------------------------------------------
|
|
605
|
+
// Recording / Bug Report WebSocket messages
|
|
606
|
+
// ---------------------------------------------------------------------------
|
|
607
|
+
|
|
608
|
+
/** Panel → Overlay (via server relay): request recording history */
|
|
609
|
+
export interface RecordingGetHistoryMessage {
|
|
610
|
+
type: 'RECORDING_GET_HISTORY';
|
|
611
|
+
to: 'overlay';
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** Overlay → Panel (via server relay): recording history response */
|
|
615
|
+
export interface RecordingHistoryMessage {
|
|
616
|
+
type: 'RECORDING_HISTORY';
|
|
617
|
+
to: 'panel';
|
|
618
|
+
snapshots: SnapshotMeta[];
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/** Panel → Overlay (via server relay): request full snapshot by ID */
|
|
622
|
+
export interface RecordingGetSnapshotMessage {
|
|
623
|
+
type: 'RECORDING_GET_SNAPSHOT';
|
|
624
|
+
to: 'overlay';
|
|
625
|
+
snapshotId: number;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Overlay → Panel (via server relay): full snapshot response */
|
|
629
|
+
export interface RecordingSnapshotMessage {
|
|
630
|
+
type: 'RECORDING_SNAPSHOT';
|
|
631
|
+
to: 'panel';
|
|
632
|
+
snapshot: RecordingSnapshot;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/** Panel → Overlay (via server relay): request range of snapshots */
|
|
636
|
+
export interface RecordingGetRangeMessage {
|
|
637
|
+
type: 'RECORDING_GET_RANGE';
|
|
638
|
+
to: 'overlay';
|
|
639
|
+
ids: number[];
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/** Overlay → Panel (via server relay): range of full snapshots */
|
|
643
|
+
export interface RecordingRangeMessage {
|
|
644
|
+
type: 'RECORDING_RANGE';
|
|
645
|
+
to: 'panel';
|
|
646
|
+
snapshots: RecordingSnapshot[];
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** Overlay → Panel (via server relay): live push of new snapshot meta */
|
|
650
|
+
export interface RecordingSnapshotMetaMessage {
|
|
651
|
+
type: 'RECORDING_SNAPSHOT_META';
|
|
652
|
+
to: 'panel';
|
|
653
|
+
meta: SnapshotMeta;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/** Panel → Overlay: enter element pick mode for bug report */
|
|
657
|
+
export interface BugReportPickElementMessage {
|
|
658
|
+
type: 'BUG_REPORT_PICK_ELEMENT';
|
|
659
|
+
to: 'overlay';
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** Overlay → Panel: element was picked for bug report */
|
|
663
|
+
export interface BugReportElementPickedMessage {
|
|
664
|
+
type: 'BUG_REPORT_ELEMENT_PICKED';
|
|
665
|
+
to: 'panel';
|
|
666
|
+
element: BugReportElement;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/** Overlay → Panel: pick mode was cancelled */
|
|
670
|
+
export interface BugReportPickCancelledMessage {
|
|
671
|
+
type: 'BUG_REPORT_PICK_CANCELLED';
|
|
672
|
+
to: 'panel';
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** Panel → Server: stage a bug-report patch */
|
|
676
|
+
export interface BugReportStageMessage {
|
|
677
|
+
type: 'BUG_REPORT_STAGE';
|
|
678
|
+
patch: Patch;
|
|
679
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { addons } from 'storybook/preview-api';
|
|
2
2
|
|
|
3
|
+
// Ghost iframes are created by AdaptiveIframe for component extraction.
|
|
4
|
+
// They must not inject the overlay or trigger story-rendered events.
|
|
5
|
+
const isGhostIframe = new URLSearchParams(window.location.search).get('vybit-ghost') === '1';
|
|
6
|
+
|
|
3
7
|
let injected = false;
|
|
4
8
|
|
|
5
9
|
export const decorators = [
|
|
6
10
|
(StoryFn: any, context: any) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (!injected) {
|
|
11
|
+
if (!isGhostIframe && !injected) {
|
|
12
|
+
const serverUrl =
|
|
13
|
+
context.parameters?.vybit?.serverUrl ?? 'http://localhost:3333';
|
|
11
14
|
const script = document.createElement('script');
|
|
12
15
|
script.src = `${serverUrl}/overlay.js`;
|
|
13
16
|
script.onerror = (err) => console.error('[vybit-addon] overlay.js FAILED to load', err);
|
|
@@ -19,12 +22,14 @@ export const decorators = [
|
|
|
19
22
|
},
|
|
20
23
|
];
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
if (!isGhostIframe) {
|
|
26
|
+
const channel = addons.getChannel();
|
|
27
|
+
let lastStoryId: string | undefined;
|
|
24
28
|
|
|
25
|
-
channel.on('storyRendered', (storyId?: string) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
29
|
+
channel.on('storyRendered', (storyId?: string) => {
|
|
30
|
+
// Only reset selection on actual story navigation, not HMR updates
|
|
31
|
+
if (storyId && storyId === lastStoryId) return;
|
|
32
|
+
lastStoryId = storyId;
|
|
33
|
+
window.postMessage({ type: 'STORYBOOK_STORY_RENDERED' }, '*');
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { addons } from '@storybook/preview-api';
|
|
2
2
|
|
|
3
|
+
// Ghost iframes are created by AdaptiveIframe for component extraction.
|
|
4
|
+
// They must not inject the overlay or trigger story-rendered events.
|
|
5
|
+
const isGhostIframe = new URLSearchParams(window.location.search).get('vybit-ghost') === '1';
|
|
6
|
+
|
|
3
7
|
let injected = false;
|
|
4
8
|
|
|
5
9
|
export const decorators = [
|
|
6
10
|
(StoryFn: any, context: any) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (!injected) {
|
|
11
|
+
if (!isGhostIframe && !injected) {
|
|
12
|
+
const serverUrl =
|
|
13
|
+
context.parameters?.vybit?.serverUrl ?? 'http://localhost:3333';
|
|
11
14
|
const script = document.createElement('script');
|
|
12
15
|
script.src = `${serverUrl}/overlay.js`;
|
|
13
16
|
script.onerror = (err) => console.error('[vybit-addon] overlay.js FAILED to load', err);
|
|
@@ -19,12 +22,14 @@ export const decorators = [
|
|
|
19
22
|
},
|
|
20
23
|
];
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
if (!isGhostIframe) {
|
|
26
|
+
const channel = addons.getChannel();
|
|
27
|
+
let lastStoryId: string | undefined;
|
|
24
28
|
|
|
25
|
-
channel.on('storyRendered', (storyId?: string) => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
29
|
+
channel.on('storyRendered', (storyId?: string) => {
|
|
30
|
+
// Only reset selection on actual story navigation, not HMR updates
|
|
31
|
+
if (storyId && storyId === lastStoryId) return;
|
|
32
|
+
lastStoryId = storyId;
|
|
33
|
+
window.postMessage({ type: 'STORYBOOK_STORY_RENDERED' }, '*');
|
|
34
|
+
});
|
|
35
|
+
}
|