@scenetest/vite-plugin 0.11.0 → 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/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/panels/observer/audio.d.ts +81 -0
- package/dist/panels/observer/audio.d.ts.map +1 -0
- package/dist/panels/observer/audio.js +296 -0
- package/dist/panels/observer/audio.js.map +1 -0
- package/dist/panels/observer/auto.d.ts +10 -0
- package/dist/panels/observer/auto.d.ts.map +1 -0
- package/dist/panels/observer/auto.js +11 -0
- package/dist/panels/observer/auto.js.map +1 -0
- package/dist/panels/observer/fs-viewer.d.ts +20 -0
- package/dist/panels/observer/fs-viewer.d.ts.map +1 -0
- package/dist/panels/observer/fs-viewer.js +536 -0
- package/dist/panels/observer/fs-viewer.js.map +1 -0
- package/dist/panels/observer/fullscreen.d.ts +24 -0
- package/dist/panels/observer/fullscreen.d.ts.map +1 -0
- package/dist/panels/observer/fullscreen.js +701 -0
- package/dist/panels/observer/fullscreen.js.map +1 -0
- package/dist/panels/observer/history.d.ts +41 -0
- package/dist/panels/observer/history.d.ts.map +1 -0
- package/dist/panels/observer/history.js +307 -0
- package/dist/panels/observer/history.js.map +1 -0
- package/dist/panels/observer/index.d.ts +33 -0
- package/dist/panels/observer/index.d.ts.map +1 -0
- package/dist/panels/observer/index.js +128 -0
- package/dist/panels/observer/index.js.map +1 -0
- package/dist/panels/observer/panel.d.ts +12 -0
- package/dist/panels/observer/panel.d.ts.map +1 -0
- package/dist/panels/observer/panel.js +461 -0
- package/dist/panels/observer/panel.js.map +1 -0
- package/dist/panels/observer/render.d.ts +109 -0
- package/dist/panels/observer/render.d.ts.map +1 -0
- package/dist/panels/observer/render.js +760 -0
- package/dist/panels/observer/render.js.map +1 -0
- package/dist/panels/observer/state.d.ts +57 -0
- package/dist/panels/observer/state.d.ts.map +1 -0
- package/dist/panels/observer/state.js +187 -0
- package/dist/panels/observer/state.js.map +1 -0
- package/dist/panels/observer/styles.d.ts +6 -0
- package/dist/panels/observer/styles.d.ts.map +1 -0
- package/dist/panels/observer/styles.js +1706 -0
- package/dist/panels/observer/styles.js.map +1 -0
- package/dist/panels/observer/types.d.ts +102 -0
- package/dist/panels/observer/types.d.ts.map +1 -0
- package/dist/panels/observer/types.js +5 -0
- package/dist/panels/observer/types.js.map +1 -0
- package/dist/panels/observer/utils.d.ts +45 -0
- package/dist/panels/observer/utils.d.ts.map +1 -0
- package/dist/panels/observer/utils.js +101 -0
- package/dist/panels/observer/utils.js.map +1 -0
- package/dist/panels/recorder/auto.d.ts +10 -0
- package/dist/panels/recorder/auto.d.ts.map +1 -0
- package/dist/panels/recorder/auto.js +11 -0
- package/dist/panels/recorder/auto.js.map +1 -0
- package/dist/panels/recorder/capture.d.ts +18 -0
- package/dist/panels/recorder/capture.d.ts.map +1 -0
- package/dist/panels/recorder/capture.js +218 -0
- package/dist/panels/recorder/capture.js.map +1 -0
- package/dist/panels/recorder/index.d.ts +41 -0
- package/dist/panels/recorder/index.d.ts.map +1 -0
- package/dist/panels/recorder/index.js +208 -0
- package/dist/panels/recorder/index.js.map +1 -0
- package/dist/panels/recorder/panel.d.ts +55 -0
- package/dist/panels/recorder/panel.d.ts.map +1 -0
- package/dist/panels/recorder/panel.js +284 -0
- package/dist/panels/recorder/panel.js.map +1 -0
- package/dist/panels/recorder/reverse-selector.d.ts +31 -0
- package/dist/panels/recorder/reverse-selector.d.ts.map +1 -0
- package/dist/panels/recorder/reverse-selector.js +116 -0
- package/dist/panels/recorder/reverse-selector.js.map +1 -0
- package/dist/panels/recorder/styles.d.ts +5 -0
- package/dist/panels/recorder/styles.d.ts.map +1 -0
- package/dist/panels/recorder/styles.js +300 -0
- package/dist/panels/recorder/styles.js.map +1 -0
- package/dist/panels/recorder/types.d.ts +51 -0
- package/dist/panels/recorder/types.d.ts.map +1 -0
- package/dist/panels/recorder/types.js +15 -0
- package/dist/panels/recorder/types.js.map +1 -0
- package/dist/strip.d.ts.map +1 -1
- package/dist/strip.js +6 -3
- package/dist/strip.js.map +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +5 -2
- package/dist/transform.js.map +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenetest Scene Recorder
|
|
3
|
+
*
|
|
4
|
+
* Records user interactions as DSL lines in a live sidebar panel.
|
|
5
|
+
* Captures clicks, typing, navigation, and annotates with inline assertions.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { initRecorder } from '@scenetest/vite-plugin/panels/recorder'
|
|
9
|
+
* initRecorder()
|
|
10
|
+
*
|
|
11
|
+
* Or auto-init:
|
|
12
|
+
* import '@scenetest/vite-plugin/panels/recorder'
|
|
13
|
+
*/
|
|
14
|
+
import { startCapture } from './capture.js';
|
|
15
|
+
import { createRecorderPanel, attachPanelListeners, appendLine, appendAnnotation, appendWarning, removeLine, clearPanel, setRecordingState, } from './panel.js';
|
|
16
|
+
/**
|
|
17
|
+
* Global recorder state
|
|
18
|
+
*/
|
|
19
|
+
const state = {
|
|
20
|
+
recording: true,
|
|
21
|
+
lines: [],
|
|
22
|
+
annotations: [],
|
|
23
|
+
nextId: 1,
|
|
24
|
+
pendingInput: null,
|
|
25
|
+
};
|
|
26
|
+
let stopCapture = null;
|
|
27
|
+
/**
|
|
28
|
+
* Initialize the scene recorder.
|
|
29
|
+
* Creates the sidebar panel, starts event capture, and hooks into assertions.
|
|
30
|
+
* Safe to call multiple times — subsequent calls are no-ops.
|
|
31
|
+
*/
|
|
32
|
+
export function initRecorder() {
|
|
33
|
+
if (typeof window === 'undefined') {
|
|
34
|
+
console.warn('[scenetest/recorder] initRecorder() called in non-browser environment');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (window.__scenetest_recorder) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
window.__scenetest_recorder = true;
|
|
41
|
+
console.log('%c\u23FA scenetest recorder', 'color: #c792ea; font-weight: bold');
|
|
42
|
+
// Create panel when DOM is ready
|
|
43
|
+
if (document.readyState === 'loading') {
|
|
44
|
+
document.addEventListener('DOMContentLoaded', setup);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
setup();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function setup() {
|
|
51
|
+
// Create the sidebar panel
|
|
52
|
+
createRecorderPanel();
|
|
53
|
+
// Wire up panel event handlers
|
|
54
|
+
attachPanelListeners({
|
|
55
|
+
onToggleRecording: () => {
|
|
56
|
+
if (!state.recording) {
|
|
57
|
+
resumeRecording();
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
onPause: () => {
|
|
61
|
+
if (state.recording) {
|
|
62
|
+
pauseRecording();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
onClear: () => {
|
|
66
|
+
state.lines = [];
|
|
67
|
+
state.annotations = [];
|
|
68
|
+
state.nextId = 1;
|
|
69
|
+
clearPanel();
|
|
70
|
+
},
|
|
71
|
+
onExport: () => {
|
|
72
|
+
exportDsl();
|
|
73
|
+
},
|
|
74
|
+
onDeleteLine: (lineId) => {
|
|
75
|
+
state.lines = state.lines.filter(l => l.id !== lineId);
|
|
76
|
+
state.annotations = state.annotations.filter(a => a.afterLineId !== lineId);
|
|
77
|
+
removeLine(lineId);
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
// Start capturing events
|
|
81
|
+
stopCapture = startCapture(state, (line) => {
|
|
82
|
+
appendLine(line, state.lines.length - 1);
|
|
83
|
+
}, (message) => {
|
|
84
|
+
appendWarning(message);
|
|
85
|
+
});
|
|
86
|
+
// Hook into assertion reporting
|
|
87
|
+
hookAssertions();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Hook into window.__scenetest_report to capture assertions
|
|
91
|
+
* and display them as inline annotations in the recorder.
|
|
92
|
+
*/
|
|
93
|
+
function hookAssertions() {
|
|
94
|
+
const existingReport = window.__scenetest_report;
|
|
95
|
+
window.__scenetest_report = function (result) {
|
|
96
|
+
// Forward to existing reporter (observer panel, Playwright, etc.)
|
|
97
|
+
if (existingReport) {
|
|
98
|
+
try {
|
|
99
|
+
existingReport(result);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Ignore errors
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Only annotate if we're recording and have lines
|
|
106
|
+
if (!state.recording || state.lines.length === 0)
|
|
107
|
+
return;
|
|
108
|
+
const lastLine = state.lines[state.lines.length - 1];
|
|
109
|
+
const annotation = {
|
|
110
|
+
afterLineId: lastLine.id,
|
|
111
|
+
type: result.result ? 'pass' : 'fail',
|
|
112
|
+
description: result.description,
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
};
|
|
115
|
+
state.annotations.push(annotation);
|
|
116
|
+
appendAnnotation(annotation);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Pause recording
|
|
121
|
+
*/
|
|
122
|
+
function pauseRecording() {
|
|
123
|
+
state.recording = false;
|
|
124
|
+
setRecordingState(false);
|
|
125
|
+
console.log('%c\u23F8 recorder paused', 'color: #6a6a8a');
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Resume recording
|
|
129
|
+
*/
|
|
130
|
+
function resumeRecording() {
|
|
131
|
+
state.recording = true;
|
|
132
|
+
setRecordingState(true);
|
|
133
|
+
console.log('%c\u23FA recorder resumed', 'color: #c792ea');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Export the current DSL lines as a downloadable .spec.md file
|
|
137
|
+
* in the formal screenplay-cue format.
|
|
138
|
+
*/
|
|
139
|
+
function exportDsl() {
|
|
140
|
+
if (state.lines.length === 0) {
|
|
141
|
+
console.warn('[scenetest/recorder] Nothing to export');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Generate scene name from current page path
|
|
145
|
+
const pagePath = location.pathname
|
|
146
|
+
.replace(/^\//, '')
|
|
147
|
+
.replace(/\//g, ' ')
|
|
148
|
+
.trim()
|
|
149
|
+
|| 'recorded scene';
|
|
150
|
+
// Build the formal markdown DSL format:
|
|
151
|
+
// ## scene-name
|
|
152
|
+
// user:
|
|
153
|
+
// - action selector
|
|
154
|
+
// - action selector value
|
|
155
|
+
const lines = [
|
|
156
|
+
`## ${pagePath}`,
|
|
157
|
+
'user:',
|
|
158
|
+
...state.lines.map(l => `- ${l.text}`),
|
|
159
|
+
'', // trailing newline
|
|
160
|
+
];
|
|
161
|
+
const content = lines.join('\n');
|
|
162
|
+
// Generate filename
|
|
163
|
+
const pathSlug = location.pathname
|
|
164
|
+
.replace(/^\//, '')
|
|
165
|
+
.replace(/\//g, '-')
|
|
166
|
+
|| 'scene';
|
|
167
|
+
const timestamp = new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-');
|
|
168
|
+
const filename = `${pathSlug}-${timestamp}.spec.md`;
|
|
169
|
+
// Create and trigger download
|
|
170
|
+
const blob = new Blob([content], { type: 'text/markdown' });
|
|
171
|
+
const url = URL.createObjectURL(blob);
|
|
172
|
+
const a = document.createElement('a');
|
|
173
|
+
a.href = url;
|
|
174
|
+
a.download = filename;
|
|
175
|
+
a.click();
|
|
176
|
+
URL.revokeObjectURL(url);
|
|
177
|
+
console.log(`%c\u2193 exported ${state.lines.length} lines to ${filename}`, 'color: #4ade80');
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get the current DSL lines as a string array.
|
|
181
|
+
* Useful for programmatic access.
|
|
182
|
+
*/
|
|
183
|
+
export function getDslLines() {
|
|
184
|
+
return state.lines.map(l => l.text);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the full recorder state.
|
|
188
|
+
* Useful for testing and debugging.
|
|
189
|
+
*/
|
|
190
|
+
export function getRecorderState() {
|
|
191
|
+
return state;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Programmatically stop the recorder and clean up.
|
|
195
|
+
*/
|
|
196
|
+
export function destroyRecorder() {
|
|
197
|
+
if (stopCapture) {
|
|
198
|
+
stopCapture();
|
|
199
|
+
stopCapture = null;
|
|
200
|
+
}
|
|
201
|
+
const el = document.getElementById('scenetest-recorder');
|
|
202
|
+
if (el) {
|
|
203
|
+
el.remove();
|
|
204
|
+
}
|
|
205
|
+
window.__scenetest_recorder = false;
|
|
206
|
+
document.documentElement.style.removeProperty('--scenetest-recorder-width');
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/panels/recorder/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar panel UI for the recorder.
|
|
3
|
+
*
|
|
4
|
+
* Shows a left-side panel with:
|
|
5
|
+
* - Recording indicator
|
|
6
|
+
* - Live DSL lines accumulating as the user interacts
|
|
7
|
+
* - Inline assertion annotations
|
|
8
|
+
* - Delete buttons on each line
|
|
9
|
+
* - Export button
|
|
10
|
+
*/
|
|
11
|
+
import type { DslLine, AssertionAnnotation } from './types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Create and mount the recorder panel
|
|
14
|
+
*/
|
|
15
|
+
export declare function createRecorderPanel(): HTMLDivElement;
|
|
16
|
+
/**
|
|
17
|
+
* Attach event listeners to the panel.
|
|
18
|
+
* Separated from creation so the caller can wire up callbacks.
|
|
19
|
+
*/
|
|
20
|
+
export declare function attachPanelListeners(opts: {
|
|
21
|
+
onToggleRecording: () => void;
|
|
22
|
+
onPause: () => void;
|
|
23
|
+
onClear: () => void;
|
|
24
|
+
onExport: () => void;
|
|
25
|
+
onDeleteLine: (lineId: number) => void;
|
|
26
|
+
}): void;
|
|
27
|
+
/**
|
|
28
|
+
* Add a new DSL line to the panel
|
|
29
|
+
*/
|
|
30
|
+
export declare function appendLine(line: DslLine, index: number): void;
|
|
31
|
+
/**
|
|
32
|
+
* Add an assertion annotation after the last line
|
|
33
|
+
*/
|
|
34
|
+
export declare function appendAnnotation(annotation: AssertionAnnotation): void;
|
|
35
|
+
/**
|
|
36
|
+
* Add a warning message to the panel
|
|
37
|
+
*/
|
|
38
|
+
export declare function appendWarning(message: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Remove a line from the panel by id
|
|
41
|
+
*/
|
|
42
|
+
export declare function removeLine(lineId: number): void;
|
|
43
|
+
/**
|
|
44
|
+
* Clear all lines and annotations from the panel
|
|
45
|
+
*/
|
|
46
|
+
export declare function clearPanel(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Update recording state indicator
|
|
49
|
+
*/
|
|
50
|
+
export declare function setRecordingState(recording: boolean): void;
|
|
51
|
+
/**
|
|
52
|
+
* Re-render all lines (after deletion or reorder)
|
|
53
|
+
*/
|
|
54
|
+
export declare function renderAllLines(lines: DslLine[], annotations: AssertionAnnotation[]): void;
|
|
55
|
+
//# sourceMappingURL=panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../../src/panels/recorder/panel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAmE9D;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAuCpD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,iBAAiB,EAAE,MAAM,IAAI,CAAA;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CACvC,GAAG,IAAI,CAqDP;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAgB7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAQtE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CASnD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAkB/C;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAUjC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAW1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE,GAAG,IAAI,CA4BzF"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar panel UI for the recorder.
|
|
3
|
+
*
|
|
4
|
+
* Shows a left-side panel with:
|
|
5
|
+
* - Recording indicator
|
|
6
|
+
* - Live DSL lines accumulating as the user interacts
|
|
7
|
+
* - Inline assertion annotations
|
|
8
|
+
* - Delete buttons on each line
|
|
9
|
+
* - Export button
|
|
10
|
+
*/
|
|
11
|
+
import { recorderStyles } from './styles.js';
|
|
12
|
+
let panelEl = null;
|
|
13
|
+
let linesEl = null;
|
|
14
|
+
let footerCountEl = null;
|
|
15
|
+
let isCollapsed = false;
|
|
16
|
+
/**
|
|
17
|
+
* Highlight DSL syntax in a line of text
|
|
18
|
+
*/
|
|
19
|
+
function highlightDsl(text) {
|
|
20
|
+
const parts = text.split(' ');
|
|
21
|
+
const action = parts[0];
|
|
22
|
+
// Actions that take only a value (no selector)
|
|
23
|
+
const valueOnlyActions = ['openTo', 'seeText', 'wait', 'emit'];
|
|
24
|
+
// Actions that take selector + value
|
|
25
|
+
const selectorValueActions = ['typeInto', 'select', 'warnIf'];
|
|
26
|
+
// No-arg actions
|
|
27
|
+
const noArgActions = ['prev', 'scrollToBottom'];
|
|
28
|
+
let html = `<span class="recorder-action">${escapeHtml(action)}</span>`;
|
|
29
|
+
if (noArgActions.includes(action) || parts.length === 1) {
|
|
30
|
+
return html;
|
|
31
|
+
}
|
|
32
|
+
if (valueOnlyActions.includes(action)) {
|
|
33
|
+
html += ` <span class="recorder-value">${escapeHtml(parts.slice(1).join(' '))}</span>`;
|
|
34
|
+
}
|
|
35
|
+
else if (selectorValueActions.includes(action)) {
|
|
36
|
+
html += ` <span class="recorder-selector">${escapeHtml(parts[1])}</span>`;
|
|
37
|
+
if (parts.length > 2) {
|
|
38
|
+
html += ` <span class="recorder-value">${escapeHtml(parts.slice(2).join(' '))}</span>`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Selector-only actions (see, click, check, notSee, seeToast, up)
|
|
43
|
+
html += ` <span class="recorder-selector">${escapeHtml(parts.slice(1).join(' '))}</span>`;
|
|
44
|
+
}
|
|
45
|
+
return html;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Render a single DSL line with `- ` prefix (formal markdown DSL format)
|
|
49
|
+
*/
|
|
50
|
+
function renderLine(line, index) {
|
|
51
|
+
return `<div class="recorder-line new" data-line-id="${line.id}">
|
|
52
|
+
<span class="recorder-line-number">${index + 1}</span>
|
|
53
|
+
<div class="recorder-line-content">
|
|
54
|
+
<span class="recorder-line-text"><span class="recorder-prefix">- </span>${highlightDsl(line.text)}</span>
|
|
55
|
+
</div>
|
|
56
|
+
<button class="recorder-line-delete" data-delete-id="${line.id}" title="Remove line">\u00d7</button>
|
|
57
|
+
</div>`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Render an assertion annotation
|
|
61
|
+
*/
|
|
62
|
+
function renderAnnotation(annotation) {
|
|
63
|
+
const icon = annotation.type === 'pass' ? '\u2713' : '\u2717';
|
|
64
|
+
return `<div class="recorder-annotation ${annotation.type}">
|
|
65
|
+
<span class="recorder-annotation-icon">${icon}</span>
|
|
66
|
+
<span class="recorder-annotation-text">${escapeHtml(annotation.description)}</span>
|
|
67
|
+
</div>`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create and mount the recorder panel
|
|
71
|
+
*/
|
|
72
|
+
export function createRecorderPanel() {
|
|
73
|
+
const el = document.createElement('div');
|
|
74
|
+
el.id = 'scenetest-recorder';
|
|
75
|
+
el.innerHTML = `
|
|
76
|
+
<style>${recorderStyles}</style>
|
|
77
|
+
<div class="recorder-header">
|
|
78
|
+
<div class="recorder-header-left">
|
|
79
|
+
<div class="recorder-rec-dot" id="recorder-rec-dot"></div>
|
|
80
|
+
<span class="recorder-header-title">scene recorder</span>
|
|
81
|
+
</div>
|
|
82
|
+
<button class="recorder-toggle-btn" id="recorder-toggle" title="Collapse">\u25C0</button>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="recorder-actions">
|
|
85
|
+
<button class="recorder-btn active" id="recorder-record-btn">record</button>
|
|
86
|
+
<button class="recorder-btn" id="recorder-pause-btn">pause</button>
|
|
87
|
+
<button class="recorder-btn danger" id="recorder-clear-btn">clear</button>
|
|
88
|
+
<button class="recorder-btn primary" id="recorder-export-btn">export .spec.md</button>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="recorder-lines" id="recorder-lines">
|
|
91
|
+
<div class="recorder-empty">
|
|
92
|
+
<div class="recorder-empty-icon">\u23FA</div>
|
|
93
|
+
Click around the app to start recording...<br>
|
|
94
|
+
Actions appear here as markdown DSL.
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="recorder-footer">
|
|
98
|
+
<span class="recorder-line-count" id="recorder-line-count">0 lines</span>
|
|
99
|
+
</div>
|
|
100
|
+
`;
|
|
101
|
+
document.body.appendChild(el);
|
|
102
|
+
panelEl = el;
|
|
103
|
+
linesEl = el.querySelector('#recorder-lines');
|
|
104
|
+
footerCountEl = el.querySelector('#recorder-line-count');
|
|
105
|
+
// Set CSS variable for app to adjust layout
|
|
106
|
+
document.documentElement.style.setProperty('--scenetest-recorder-width', '360px');
|
|
107
|
+
return el;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Attach event listeners to the panel.
|
|
111
|
+
* Separated from creation so the caller can wire up callbacks.
|
|
112
|
+
*/
|
|
113
|
+
export function attachPanelListeners(opts) {
|
|
114
|
+
if (!panelEl)
|
|
115
|
+
return;
|
|
116
|
+
// Toggle collapse
|
|
117
|
+
panelEl.querySelector('#recorder-toggle')?.addEventListener('click', (e) => {
|
|
118
|
+
e.stopPropagation();
|
|
119
|
+
isCollapsed = !isCollapsed;
|
|
120
|
+
panelEl?.classList.toggle('collapsed', isCollapsed);
|
|
121
|
+
const btn = panelEl?.querySelector('#recorder-toggle');
|
|
122
|
+
if (btn) {
|
|
123
|
+
btn.textContent = isCollapsed ? '\u25B6' : '\u25C0';
|
|
124
|
+
btn.setAttribute('title', isCollapsed ? 'Expand' : 'Collapse');
|
|
125
|
+
}
|
|
126
|
+
document.documentElement.style.setProperty('--scenetest-recorder-width', isCollapsed ? '48px' : '360px');
|
|
127
|
+
});
|
|
128
|
+
// Record button
|
|
129
|
+
panelEl.querySelector('#recorder-record-btn')?.addEventListener('click', (e) => {
|
|
130
|
+
e.stopPropagation();
|
|
131
|
+
opts.onToggleRecording();
|
|
132
|
+
});
|
|
133
|
+
// Pause button
|
|
134
|
+
panelEl.querySelector('#recorder-pause-btn')?.addEventListener('click', (e) => {
|
|
135
|
+
e.stopPropagation();
|
|
136
|
+
opts.onPause();
|
|
137
|
+
});
|
|
138
|
+
// Clear button
|
|
139
|
+
panelEl.querySelector('#recorder-clear-btn')?.addEventListener('click', (e) => {
|
|
140
|
+
e.stopPropagation();
|
|
141
|
+
opts.onClear();
|
|
142
|
+
});
|
|
143
|
+
// Export button
|
|
144
|
+
panelEl.querySelector('#recorder-export-btn')?.addEventListener('click', (e) => {
|
|
145
|
+
e.stopPropagation();
|
|
146
|
+
opts.onExport();
|
|
147
|
+
});
|
|
148
|
+
// Delete line (event delegation on the lines container)
|
|
149
|
+
linesEl?.addEventListener('click', (e) => {
|
|
150
|
+
const target = e.target;
|
|
151
|
+
const deleteBtn = target.closest('[data-delete-id]');
|
|
152
|
+
if (deleteBtn) {
|
|
153
|
+
e.stopPropagation();
|
|
154
|
+
const lineId = parseInt(deleteBtn.getAttribute('data-delete-id'), 10);
|
|
155
|
+
opts.onDeleteLine(lineId);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Add a new DSL line to the panel
|
|
161
|
+
*/
|
|
162
|
+
export function appendLine(line, index) {
|
|
163
|
+
if (!linesEl)
|
|
164
|
+
return;
|
|
165
|
+
// Remove the empty state message if present
|
|
166
|
+
const emptyEl = linesEl.querySelector('.recorder-empty');
|
|
167
|
+
if (emptyEl) {
|
|
168
|
+
emptyEl.remove();
|
|
169
|
+
}
|
|
170
|
+
const lineHtml = renderLine(line, index);
|
|
171
|
+
linesEl.insertAdjacentHTML('beforeend', lineHtml);
|
|
172
|
+
// Scroll to bottom
|
|
173
|
+
linesEl.scrollTop = linesEl.scrollHeight;
|
|
174
|
+
updateLineCount(index + 1);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Add an assertion annotation after the last line
|
|
178
|
+
*/
|
|
179
|
+
export function appendAnnotation(annotation) {
|
|
180
|
+
if (!linesEl)
|
|
181
|
+
return;
|
|
182
|
+
const html = renderAnnotation(annotation);
|
|
183
|
+
linesEl.insertAdjacentHTML('beforeend', html);
|
|
184
|
+
// Scroll to bottom
|
|
185
|
+
linesEl.scrollTop = linesEl.scrollHeight;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Add a warning message to the panel
|
|
189
|
+
*/
|
|
190
|
+
export function appendWarning(message) {
|
|
191
|
+
if (!linesEl)
|
|
192
|
+
return;
|
|
193
|
+
const html = `<div class="recorder-warning">
|
|
194
|
+
<span class="recorder-warning-icon">\u26A0</span>
|
|
195
|
+
<span>${escapeHtml(message)}</span>
|
|
196
|
+
</div>`;
|
|
197
|
+
linesEl.insertAdjacentHTML('beforeend', html);
|
|
198
|
+
linesEl.scrollTop = linesEl.scrollHeight;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Remove a line from the panel by id
|
|
202
|
+
*/
|
|
203
|
+
export function removeLine(lineId) {
|
|
204
|
+
if (!linesEl)
|
|
205
|
+
return;
|
|
206
|
+
const lineEl = linesEl.querySelector(`[data-line-id="${lineId}"]`);
|
|
207
|
+
if (lineEl) {
|
|
208
|
+
lineEl.remove();
|
|
209
|
+
}
|
|
210
|
+
// Re-number remaining lines
|
|
211
|
+
const lines = linesEl.querySelectorAll('.recorder-line');
|
|
212
|
+
lines.forEach((el, i) => {
|
|
213
|
+
const numEl = el.querySelector('.recorder-line-number');
|
|
214
|
+
if (numEl) {
|
|
215
|
+
numEl.textContent = String(i + 1);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
updateLineCount(lines.length);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Clear all lines and annotations from the panel
|
|
222
|
+
*/
|
|
223
|
+
export function clearPanel() {
|
|
224
|
+
if (!linesEl)
|
|
225
|
+
return;
|
|
226
|
+
linesEl.innerHTML = `<div class="recorder-empty">
|
|
227
|
+
<div class="recorder-empty-icon">\u23FA</div>
|
|
228
|
+
Click around the app to start recording...<br>
|
|
229
|
+
Actions appear here as markdown DSL.
|
|
230
|
+
</div>`;
|
|
231
|
+
updateLineCount(0);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Update recording state indicator
|
|
235
|
+
*/
|
|
236
|
+
export function setRecordingState(recording) {
|
|
237
|
+
if (!panelEl)
|
|
238
|
+
return;
|
|
239
|
+
const dot = panelEl.querySelector('#recorder-rec-dot');
|
|
240
|
+
const recordBtn = panelEl.querySelector('#recorder-record-btn');
|
|
241
|
+
const pauseBtn = panelEl.querySelector('#recorder-pause-btn');
|
|
242
|
+
dot?.classList.toggle('paused', !recording);
|
|
243
|
+
recordBtn?.classList.toggle('active', recording);
|
|
244
|
+
pauseBtn?.classList.toggle('active', !recording);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Re-render all lines (after deletion or reorder)
|
|
248
|
+
*/
|
|
249
|
+
export function renderAllLines(lines, annotations) {
|
|
250
|
+
if (!linesEl)
|
|
251
|
+
return;
|
|
252
|
+
if (lines.length === 0) {
|
|
253
|
+
clearPanel();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
let html = '';
|
|
257
|
+
for (let i = 0; i < lines.length; i++) {
|
|
258
|
+
html += renderLine(lines[i], i);
|
|
259
|
+
// Add any annotations that belong after this line
|
|
260
|
+
const lineAnnotations = annotations.filter(a => a.afterLineId === lines[i].id);
|
|
261
|
+
for (const ann of lineAnnotations) {
|
|
262
|
+
html += renderAnnotation(ann);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Add any trailing annotations (after the last line)
|
|
266
|
+
const lastLineId = lines[lines.length - 1].id;
|
|
267
|
+
const trailingAnnotations = annotations.filter(a => a.afterLineId > lastLineId);
|
|
268
|
+
for (const ann of trailingAnnotations) {
|
|
269
|
+
html += renderAnnotation(ann);
|
|
270
|
+
}
|
|
271
|
+
linesEl.innerHTML = html;
|
|
272
|
+
updateLineCount(lines.length);
|
|
273
|
+
}
|
|
274
|
+
function updateLineCount(count) {
|
|
275
|
+
if (footerCountEl) {
|
|
276
|
+
footerCountEl.textContent = `${count} line${count !== 1 ? 's' : ''}`;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function escapeHtml(text) {
|
|
280
|
+
const div = document.createElement('div');
|
|
281
|
+
div.textContent = text;
|
|
282
|
+
return div.innerHTML;
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../../../src/panels/recorder/panel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reverse selector resolution: given a DOM element, produce a scenetest selector string.
|
|
3
|
+
*
|
|
4
|
+
* This is the inverse of resolveSelector() in @scenetest/cli.
|
|
5
|
+
* Instead of selector → element, it goes element → selector.
|
|
6
|
+
*
|
|
7
|
+
* Algorithm:
|
|
8
|
+
* 1. Starting from the target element, find the first addressable attribute
|
|
9
|
+
* 2. Walk up ancestors collecting addressable tokens to build a unique path
|
|
10
|
+
* 3. Return the space-separated selector string (parent child grandchild)
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a DOM element to a scenetest selector string.
|
|
14
|
+
*
|
|
15
|
+
* Walks up from the element collecting addressable tokens until
|
|
16
|
+
* the path uniquely identifies the element.
|
|
17
|
+
*
|
|
18
|
+
* @returns The selector string, or null if the element can't be addressed
|
|
19
|
+
*/
|
|
20
|
+
export declare function reverseSelector(element: Element): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Find the nearest addressable ancestor (or self) for an element.
|
|
23
|
+
* Useful when the clicked element itself has no selector but a parent does.
|
|
24
|
+
*/
|
|
25
|
+
export declare function nearestAddressable(element: Element): Element | null;
|
|
26
|
+
/**
|
|
27
|
+
* Get a human-readable description of why an element can't be addressed.
|
|
28
|
+
* Used to show warnings when clicking unaddressable elements.
|
|
29
|
+
*/
|
|
30
|
+
export declare function explainUnaddressable(element: Element): string;
|
|
31
|
+
//# sourceMappingURL=reverse-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reverse-selector.d.ts","sourceRoot":"","sources":["../../../src/panels/recorder/reverse-selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAqCH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAoC/D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAWnE;AAgBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAK7D"}
|