superdoc-macros 0.7.0 → 0.7.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/README.md +5 -1
- package/dist/manager.d.ts +7 -1
- package/dist/manager.js +21 -6
- package/dist/messages.d.ts +6 -0
- package/dist/messages.js +2 -0
- package/dist/recorder/recorder.d.ts +7 -0
- package/dist/recorder/recorder.js +24 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,7 +87,9 @@ If you must waive isolation (e.g. a CSP that blocks `srcdoc`), switch to the dir
|
|
|
87
87
|
## 2. Macro recorder
|
|
88
88
|
|
|
89
89
|
```ts
|
|
90
|
-
kit.startRecording()
|
|
90
|
+
if (!kit.startRecording()) {
|
|
91
|
+
throw new Error('A macro is running or an unsaved recording is pending');
|
|
92
|
+
}
|
|
91
93
|
// the user works normally: typing, formatting, lists...
|
|
92
94
|
const recording = kit.stopRecording('Standard intro', 'Ctrl+Alt+1');
|
|
93
95
|
|
|
@@ -97,6 +99,8 @@ await kit.replayRecording(recording.id);
|
|
|
97
99
|
|
|
98
100
|
The recorder captures **commands and typing**, not caret positions — exactly like Word's recorder: replay applies wherever the caret stands. Consecutive keystrokes coalesce into one step, `undo`/`redo` are not recorded (configurable via `RecorderOptions`), and recordings persist as clean JSON that can be exported and shared. `updateRecording({id, name?, shortcut?})` renames a recording or edits its shortcut.
|
|
99
101
|
|
|
102
|
+
Finalization is loss-aware. A command payload that cannot be stored faithfully is reported instead of silently omitted; `stopRecording()` keeps the stopped capture pending until it is saved or explicitly cancelled. A mixed capture requires `{ allowIncomplete: true }` after the host has obtained user consent, while a capture containing no replayable step is rejected as `recording-uncapturable`. Storage, capacity, and validation failures are retryable: fix the problem and call `stopRecording()` again. Starting a new recording while one is pending is rejected, preventing accidental loss.
|
|
103
|
+
|
|
100
104
|
## 3. Snippets and auto-text
|
|
101
105
|
|
|
102
106
|
```ts
|
package/dist/manager.d.ts
CHANGED
|
@@ -108,13 +108,19 @@ export declare class MacroKit {
|
|
|
108
108
|
runSource(source: string): Promise<MacroRunResult>;
|
|
109
109
|
get isRecording(): boolean;
|
|
110
110
|
get recordedStepCount(): number;
|
|
111
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Starts a fresh recording. Returns false instead of overwriting an active
|
|
113
|
+
* or stopped-but-unsaved capture, or while another macro is running.
|
|
114
|
+
*/
|
|
115
|
+
startRecording(): boolean;
|
|
112
116
|
/**
|
|
113
117
|
* Stops and saves. `null` when no step was recorded — there is nothing to
|
|
114
118
|
* save.
|
|
115
119
|
*
|
|
116
120
|
* Throws — with the stopped recording **retained for retry** (call again;
|
|
117
121
|
* `cancelRecording` is the explicit way to drop it) — when:
|
|
122
|
+
* - `recording-uncapturable`: every action was uncapturable, so even an
|
|
123
|
+
* explicitly incomplete save would create an empty macro.
|
|
118
124
|
* - `recording-incomplete`: some actions could not be captured (e.g. an
|
|
119
125
|
* inserted image, whose payload is the whole file). Saving that as-is
|
|
120
126
|
* would present a macro that replays less than what the user did, so it
|
package/dist/manager.js
CHANGED
|
@@ -223,10 +223,15 @@ export class MacroKit {
|
|
|
223
223
|
get recordedStepCount() {
|
|
224
224
|
return this.recorder.stepCount;
|
|
225
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Starts a fresh recording. Returns false instead of overwriting an active
|
|
228
|
+
* or stopped-but-unsaved capture, or while another macro is running.
|
|
229
|
+
*/
|
|
226
230
|
startRecording() {
|
|
227
|
-
if (this.running)
|
|
228
|
-
return;
|
|
231
|
+
if (this.running || this.recorder.recording || this.recorder.hasPending)
|
|
232
|
+
return false;
|
|
229
233
|
this.recorder.start();
|
|
234
|
+
return this.recorder.recording;
|
|
230
235
|
}
|
|
231
236
|
/**
|
|
232
237
|
* Stops and saves. `null` when no step was recorded — there is nothing to
|
|
@@ -234,6 +239,8 @@ export class MacroKit {
|
|
|
234
239
|
*
|
|
235
240
|
* Throws — with the stopped recording **retained for retry** (call again;
|
|
236
241
|
* `cancelRecording` is the explicit way to drop it) — when:
|
|
242
|
+
* - `recording-uncapturable`: every action was uncapturable, so even an
|
|
243
|
+
* explicitly incomplete save would create an empty macro.
|
|
237
244
|
* - `recording-incomplete`: some actions could not be captured (e.g. an
|
|
238
245
|
* inserted image, whose payload is the whole file). Saving that as-is
|
|
239
246
|
* would present a macro that replays less than what the user did, so it
|
|
@@ -243,19 +250,27 @@ export class MacroKit {
|
|
|
243
250
|
* - a persistence failure (quota, oversized state).
|
|
244
251
|
*/
|
|
245
252
|
stopRecording(name, shortcut, options = {}) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
253
|
+
// Stop first. Every failure below must leave the recorder inactive and
|
|
254
|
+
// its snapshot retryable. Checking capacity before stop() used to leave a
|
|
255
|
+
// manual recording active behind a stopped UI, and could lose an
|
|
256
|
+
// auto-stopped recording on the next start.
|
|
249
257
|
const pending = this.recorder.stop();
|
|
250
258
|
const { steps, truncated } = splitOversizedSteps(pending.steps);
|
|
251
259
|
if (truncated)
|
|
252
260
|
throw new MacroError(macroMessages().recordingTooLarge, 'recording-too-large');
|
|
261
|
+
const commandIds = [...new Set(pending.warnings.map((warning) => warning.commandId))].join(', ');
|
|
262
|
+
if (pending.warnings.length > 0 && steps.length === 0) {
|
|
263
|
+
const messages = macroMessages();
|
|
264
|
+
throw new MacroError(messages.recordingUncapturable?.(commandIds) ?? messages.recordingIncomplete(commandIds), 'recording-uncapturable');
|
|
265
|
+
}
|
|
253
266
|
if (steps.length === 0) {
|
|
254
267
|
this.recorder.discard();
|
|
255
268
|
return null;
|
|
256
269
|
}
|
|
270
|
+
this.requireValidShortcut(shortcut);
|
|
271
|
+
this.requireItemLimits({ name, shortcut });
|
|
272
|
+
this.requireRoom(this.state.recordings);
|
|
257
273
|
if (pending.warnings.length > 0 && !options.allowIncomplete) {
|
|
258
|
-
const commandIds = [...new Set(pending.warnings.map((warning) => warning.commandId))].join(', ');
|
|
259
274
|
throw new MacroError(macroMessages().recordingIncomplete(commandIds), 'recording-incomplete');
|
|
260
275
|
}
|
|
261
276
|
const recording = {
|
package/dist/messages.d.ts
CHANGED
|
@@ -43,6 +43,12 @@ export interface MacroMessages {
|
|
|
43
43
|
saveFailed: string;
|
|
44
44
|
recordingTooLarge: string;
|
|
45
45
|
recordingIncomplete: (commandIds: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* No usable step was captured; saving with allowIncomplete would create an
|
|
48
|
+
* empty macro. Optional for source compatibility with existing full locale
|
|
49
|
+
* objects compiled against 0.7.0.
|
|
50
|
+
*/
|
|
51
|
+
recordingUncapturable?: (commandIds: string) => string;
|
|
46
52
|
shortcutInvalid: string;
|
|
47
53
|
shortcutNeedsModifier: string;
|
|
48
54
|
shortcutReserved: string;
|
package/dist/messages.js
CHANGED
|
@@ -43,6 +43,7 @@ export const ENGLISH_MESSAGES = {
|
|
|
43
43
|
saveFailed: 'Saving failed — the change was not applied',
|
|
44
44
|
recordingTooLarge: 'The recording is too large to save',
|
|
45
45
|
recordingIncomplete: (commandIds) => `The recording is missing actions that cannot be recorded (${commandIds})`,
|
|
46
|
+
recordingUncapturable: (commandIds) => `The recording contains only actions that cannot be recorded (${commandIds})`,
|
|
46
47
|
shortcutInvalid: 'Invalid shortcut — use a form like Ctrl+Alt+M',
|
|
47
48
|
shortcutNeedsModifier: 'A shortcut must include Ctrl, Alt or Meta',
|
|
48
49
|
shortcutReserved: 'This shortcut is reserved by the editor',
|
|
@@ -83,6 +84,7 @@ export const HEBREW_MESSAGES = {
|
|
|
83
84
|
saveFailed: 'השמירה נכשלה — השינוי לא הוחל',
|
|
84
85
|
recordingTooLarge: 'ההקלטה גדולה מכדי להישמר',
|
|
85
86
|
recordingIncomplete: (commandIds) => `בהקלטה חסרות פעולות שאינן ניתנות להקלטה (${commandIds})`,
|
|
87
|
+
recordingUncapturable: (commandIds) => `ההקלטה מכילה רק פעולות שאינן ניתנות להקלטה (${commandIds})`,
|
|
86
88
|
shortcutInvalid: 'קיצור לא תקין — הצורה הנדרשת היא למשל Ctrl+Alt+M',
|
|
87
89
|
shortcutNeedsModifier: 'קיצור חייב לכלול Ctrl, Alt או Meta',
|
|
88
90
|
shortcutReserved: 'הקיצור הזה שמור לעורך',
|
|
@@ -80,6 +80,13 @@ export declare class MacroRecorder {
|
|
|
80
80
|
applyAutoTextExpansion(consumed: number, replacement: string): void;
|
|
81
81
|
private teardown;
|
|
82
82
|
private push;
|
|
83
|
+
private addWarning;
|
|
84
|
+
/**
|
|
85
|
+
* Warnings are part of the stopped snapshot just like steps. Counting both
|
|
86
|
+
* prevents a warning-only recording from growing without bound while never
|
|
87
|
+
* reaching the ordinary step cap.
|
|
88
|
+
*/
|
|
89
|
+
private maybeAutoStop;
|
|
83
90
|
/**
|
|
84
91
|
* Records a programmatic insertion the host will not report as typing —
|
|
85
92
|
* e.g. a snippet expanded from a button or shortcut, which writes through
|
|
@@ -146,12 +146,27 @@ export class MacroRecorder {
|
|
|
146
146
|
}
|
|
147
147
|
push(step) {
|
|
148
148
|
this.steps.push(step);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
this.maybeAutoStop();
|
|
150
|
+
}
|
|
151
|
+
addWarning(warning) {
|
|
152
|
+
this.warnings.push(warning);
|
|
153
|
+
this.maybeAutoStop();
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Warnings are part of the stopped snapshot just like steps. Counting both
|
|
157
|
+
* prevents a warning-only recording from growing without bound while never
|
|
158
|
+
* reaching the ordinary step cap.
|
|
159
|
+
*/
|
|
160
|
+
maybeAutoStop() {
|
|
161
|
+
if (!this.active)
|
|
162
|
+
return;
|
|
163
|
+
if (this.steps.length + this.warnings.length < this.maxSteps)
|
|
164
|
+
return;
|
|
165
|
+
this.teardown();
|
|
166
|
+
this.pending = { steps: this.steps, warnings: this.warnings };
|
|
167
|
+
this.steps = [];
|
|
168
|
+
this.warnings = [];
|
|
169
|
+
this.onAutoStop?.();
|
|
155
170
|
}
|
|
156
171
|
/**
|
|
157
172
|
* Records a programmatic insertion the host will not report as typing —
|
|
@@ -184,15 +199,15 @@ export class MacroRecorder {
|
|
|
184
199
|
json = JSON.stringify(payload);
|
|
185
200
|
}
|
|
186
201
|
catch {
|
|
187
|
-
this.
|
|
202
|
+
this.addWarning({ commandId: id, reason: 'payload-not-serializable' });
|
|
188
203
|
return;
|
|
189
204
|
}
|
|
190
205
|
if (typeof json !== 'string') {
|
|
191
|
-
this.
|
|
206
|
+
this.addWarning({ commandId: id, reason: 'payload-not-serializable' });
|
|
192
207
|
return;
|
|
193
208
|
}
|
|
194
209
|
if (json.length > IMPORT_LIMITS.maxPayloadLength) {
|
|
195
|
-
this.
|
|
210
|
+
this.addWarning({ commandId: id, reason: 'payload-too-large' });
|
|
196
211
|
return;
|
|
197
212
|
}
|
|
198
213
|
this.push({ type: 'command', id, payload: JSON.parse(json) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superdoc-macros",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Macro toolkit for SuperDoc-based editors: sandboxed scripted macros, a Word-style macro recorder, and snippets with auto-text expansion.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|