superdoc-macros 0.2.0 → 0.3.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 CHANGED
@@ -1,61 +1,54 @@
1
1
  # superdoc-macros
2
2
 
3
- ערכת מאקרו לעורכים מבוססי **SuperDoc v2**. נכתבה במקור עבור [otzaria-word-editor](https://github.com/Y-PLONI/otzaria-word-editor), אך כללית לחלוטין: אין תלות בו ואף לא בחבילת superdoc עצמה (החיבור למנוע מבני, דרך המשטחים הציבוריים שלו), והליבה עובדת מול כל עורך שמממש ממשק `MacroHost` קטן.
3
+ A macro toolkit for **SuperDoc v2**-based editors. Originally built for [otzaria-word-editor](https://github.com/Y-PLONI/otzaria-word-editor), but fully generic: it has no dependency on that project — nor on the superdoc package itself (the engine is consumed structurally, through its public surfaces), and the core works against any editor that implements a small `MacroHost` interface.
4
4
 
5
- שלוש יכולות, בדומה למאקרו של Word:
5
+ Three capabilities, in the spirit of Word macros:
6
6
 
7
- | יכולת | מה זה נותן |
7
+ | Capability | What it gives you |
8
8
  | --- | --- |
9
- | **סקריפטים** | מאקרו כתובים ב-JavaScript שרצים בארגז חול (iframe מבודד) מול API מצומצם ובטוח של המסמך |
10
- | **מקליט מאקרו** | "הקלטעשה פעולותעצורנגן" — מקליט פקודות והקלדה, כמו המקליט של Word |
11
- | **קטעי טקסט (Snippets)** | תבניות עם משתנים (`{{date}}`, `{{selection}}`…), קיצורי מקלדת, והשלמה אוטומטית בהקלדה (הקלדת `בסד` + רווח `בס"ד`) |
9
+ | **Scripted macros** | User-written JavaScript macros that run in a real sandbox (an isolated iframe) against a small, safe document API |
10
+ | **Macro recorder** | "Recordwork normallystopreplay" — records commands and typing, like Word's recorder |
11
+ | **Snippets (AutoText)** | Templates with variables (`{{date}}`, `{{selection}}`…), keyboard shortcuts, and auto-expansion while typing (type a trigger word + space) |
12
12
 
13
- בנוסף: שמירה מתמשכת (localStorage או אחסון מותאם), ייבוא/ייצוא JSON, וקישור קיצורי מקלדת.
13
+ Plus: persistence (localStorage or custom storage), JSON import/export, keyboard shortcut binding, and localizable runtime messages (English by default, Hebrew locale included).
14
14
 
15
- > **הערה על VBA:** החבילה אינה מריצה מאקרו VBA מתוך קובצי `.docm` — אין מנוע VBA בדפדפן. היא נותנת מערכת מאקרו מקבילה, מבוססת JavaScript, שמתאימה לעורך רץ-בדפדפן.
15
+ > **A note on VBA:** the toolkit does not execute VBA macros from `.docm` files there is no VBA engine in the browser. It provides a parallel, JavaScript-based macro system suited to a browser-hosted editor.
16
16
 
17
- ## התקנה
17
+ ## Installation
18
18
 
19
19
  ```bash
20
20
  npm install superdoc-macros
21
21
  ```
22
22
 
23
- או ישירות מגיטהאב (עד הפרסום ב-npm):
24
-
25
- ```bash
26
- npm install github:palmoni5/superdoc-macros
27
- ```
28
-
29
- ## התחלה מהירה (עם SuperDoc)
23
+ ## Quick start (with SuperDoc)
30
24
 
31
25
  ```ts
32
26
  import { MacroKit, createSuperdocHost } from 'superdoc-macros';
33
27
 
34
- // superdoc — מופע SuperDoc מוכן (אחרי onReady); container — האלמנט שהמסמך מרונדר בו.
28
+ // superdoc — a ready SuperDoc instance (after onReady); container — the element the document renders in.
35
29
  const host = createSuperdocHost({ superdoc, container });
36
30
  const kit = new MacroKit({ host });
37
31
 
38
- // קיצורי מקלדת לכל מה ששמור, והשלמה אוטומטית:
32
+ // Keyboard shortcuts for everything saved, and auto-text:
39
33
  const unbindKeys = kit.attachShortcuts(container);
40
34
  const disableAutoText = kit.enableAutoText();
41
35
 
42
- // בהחלפת מסמך / פירוק:
36
+ // On document swap / teardown:
43
37
  unbindKeys();
44
38
  disableAutoText();
45
39
  host.dispose();
46
40
  ```
47
41
 
48
- ## 1. מאקרו כתובים (סקריפטים)
42
+ ## 1. Scripted macros
49
43
 
50
- סקריפט מקבל אובייקט `api` וכל המתודות שלו א-סינכרוניות:
44
+ A script receives an `api` object; every method is async:
51
45
 
52
46
  ```ts
53
47
  kit.saveScript({
54
- name: 'כותרת דבר תורה',
48
+ name: 'Heading helper',
55
49
  shortcut: 'Ctrl+Alt+D',
56
50
  source: `
57
51
  await api.bold();
58
- await api.insertText('בעניין ');
59
52
  const selected = await api.getSelectionText();
60
53
  if (selected) await api.insertText(selected);
61
54
  await api.bold();
@@ -67,91 +60,101 @@ const result = await kit.runScript(kit.listScripts()[0].id);
67
60
  if (!result.ok) console.warn(result.message);
68
61
  ```
69
62
 
70
- ### ה-API שסקריפט מקבל
63
+ ### The script API
71
64
 
72
- | מתודה | תיאור |
65
+ | Method | Description |
73
66
  | --- | --- |
74
- | `api.command(id, payload?)` | כל פקודה מקטלוג SuperDoc (`'text-align'`, `'font-size'`…). מחזירה `{ok}` ואינה זורקת |
75
- | `api.hasCommand(id)` / `api.commandIds()` | בירור יכולות |
76
- | `api.insertText(text)` / `api.insertParagraph()` | הכנסה במיקום הסמן |
77
- | `api.deleteBackward(count?)` | מחיקה לאחור |
78
- | `api.getSelection()` / `api.getSelectionText()` | הבחירה הנוכחית |
79
- | `api.getDocumentText()` | הטקסט המלא |
80
- | `api.replaceAll(find, replace)` | החלפה גורפת; מחזירה כמה הוחלפו |
81
- | `api.bold()` / `italic()` / `underline()` / `bulletList()` / `directionRtl()` … | סוכר לפקודות נפוצותזורקות בכשל, כדי שהסקריפט ייעצר |
82
- | `api.log(...)` | יומן ריצה (מגיע ל-`onLog` של ה-Kit) |
67
+ | `api.command(id, payload?)` | Any command from the SuperDoc catalog (`'text-align'`, `'font-size'`…). Returns `{ok}`, never throws |
68
+ | `api.hasCommand(id)` / `api.commandIds()` | Capability discovery |
69
+ | `api.insertText(text)` / `api.insertParagraph()` | Insert at the caret |
70
+ | `api.deleteBackward(count?)` | Delete backwards |
71
+ | `api.getSelection()` / `api.getSelectionText()` | The current selection |
72
+ | `api.getDocumentText()` | The full document text |
73
+ | `api.replaceAll(find, replace)` | Replace everywhere; returns the count |
74
+ | `api.bold()` / `italic()` / `underline()` / `bulletList()` / `directionRtl()` … | Sugar for common commands these throw on failure, so the script stops |
75
+ | `api.log(...)` | Run log (delivered to the kit's `onLog`) |
83
76
 
84
- ### אבטחה
77
+ ### Security
85
78
 
86
- ברירת המחדל היא **ארגז חול אמיתי**: הסקריפט רץ ב-iframe עם `sandbox="allow-scripts"` בלבד — origin אטום, בלי גישה ל-DOM של האפליקציה, ל-localStorage, ל-cookies או לרשת עם אישורי המשתמש. הדרך היחידה שלו לגעת במסמך היא ה-API שלמעלה, עם תקרת זמן (ברירת מחדל 30 שניותנאכפת גם על לולאה אינסופית, ע"י הסרת ה-iframe) ותקרת קריאות (10,000).
79
+ The default is a **real sandbox**: scripts run in an iframe with `sandbox="allow-scripts"` onlyan opaque origin, no access to the application's DOM, localStorage, cookies, or credentialed network. The script's only way to touch the document is the API above, with a time cap (default 30 senforced even against infinite loops, by removing the iframe) and a call cap (10,000).
87
80
 
88
- מי שחייב לוותר על הבידוד (למשל CSP שחוסם `srcdoc`) יכול לעבור למריץ ישיר: `new MacroKit({ host, runner: 'eval' })` — ראו האזהרות בקוד.
81
+ If you must waive isolation (e.g. a CSP that blocks `srcdoc`), switch to the direct runner: `new MacroKit({ host, runner: 'eval' })` — see the warnings in the code.
89
82
 
90
- ## 2. מקליט מאקרו
83
+ ## 2. Macro recorder
91
84
 
92
85
  ```ts
93
86
  kit.startRecording();
94
- // המשתמש עובד רגיל: מקליד, מדגיש, ממספר...
95
- const recording = kit.stopRecording('פתיח סטנדרטי', 'Ctrl+Alt+1');
87
+ // the user works normally: typing, formatting, lists...
88
+ const recording = kit.stopRecording('Standard intro', 'Ctrl+Alt+1');
96
89
 
97
- // מאוחר יותר, מכל מקום במסמך:
90
+ // later, from anywhere in the document:
98
91
  await kit.replayRecording(recording.id);
99
92
  ```
100
93
 
101
- המקליט מתעד **פקודות והקלדה**, לא מיקומי סמן בדיוק כמו המקליט של Word: הניגון חל במקום שבו הסמן עומד. הקשות רצופות מתלכדות לצעד אחד, `undo`/`redo` אינם מוקלטים (ניתן לשינוי ב-`RecorderOptions`), וההקלטה נשמרת כ-JSON נקי שאפשר לייצא ולשתף.
94
+ 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.
102
95
 
103
- ## 3. קטעי טקסט והשלמה אוטומטית
96
+ ## 3. Snippets and auto-text
104
97
 
105
98
  ```ts
106
- kit.saveSnippet({ name: 'בס"ד', text: 'בס"ד', trigger: 'בסד' });
99
+ kit.saveSnippet({ name: 'BSD', text: 'בס"ד', trigger: 'בסד' });
107
100
  kit.saveSnippet({
108
- name: 'חתימה',
109
- text: 'ונשלם בעז"ה, {{date}}',
101
+ name: 'Signature',
102
+ text: 'Best regards, {{date}}',
110
103
  shortcut: 'Ctrl+Alt+S',
111
104
  });
112
- kit.saveSnippet({ name: 'ציטוט', text: '(עיין {{selection}})' });
105
+ kit.saveSnippet({ name: 'Citation', text: '(see {{selection}})' });
113
106
 
114
- kit.enableAutoText(); // מעכשיו: הקלדת "בסד" + רווח בס"ד
115
- await kit.expandSnippet(id); // או הרחבה יזומה / דרך הקיצור
107
+ kit.enableAutoText(); // from now on: typing the trigger + space expands it
108
+ await kit.expandSnippet(id); // or expand explicitly / via the shortcut
116
109
  ```
117
110
 
118
- משתנים מובנים: `{{date}}`, `{{time}}`, `{{datetime}}` (עברית), `{{selection}}`. כל שם אחר נפתר מ-`variables` שנמסרו ל-`expandSnippet`; משתנה ללא ערך נשאר גלוי בטקסט.
111
+ Built-in variables: `{{date}}`, `{{time}}`, `{{datetime}}` (formatted with the browser locale, or an explicit `locale` option), `{{selection}}`. Any other name resolves from the `variables` passed to `expandSnippet`; a variable with no value stays visible in the text.
112
+
113
+ ## Localization
114
+
115
+ Runtime messages (failures shown to end users) default to English. A host with a localized UI swaps them once at startup:
116
+
117
+ ```ts
118
+ import { setMacroMessages, HEBREW_MESSAGES } from 'superdoc-macros';
119
+
120
+ setMacroMessages(HEBREW_MESSAGES); // full Hebrew locale (included)
121
+ setMacroMessages({ scriptNotFound: '…' }); // or a partial override
122
+ ```
119
123
 
120
- ## שמירה, ייבוא וייצוא
124
+ ## Persistence, import and export
121
125
 
122
126
  ```ts
123
127
  import { createLocalStorage } from 'superdoc-macros';
124
128
 
125
129
  const kit = new MacroKit({ host, storage: createLocalStorage('my-key') });
126
130
 
127
- const json = kit.exportState(); // גיבוי / שיתוף
131
+ const json = kit.exportState(); // backup / sharing
128
132
  kit.importState(json, { merge: true });
129
133
  ```
130
134
 
131
- `MacroStorage` הוא ממשק בן שתי מתודות אפשר לממש שמירה לקובץ (למשל ב-workspace של תוסף אוצריא).
135
+ `MacroStorage` is a two-method interfaceimplement it to persist to a file (e.g. a plugin workspace).
132
136
 
133
- ## חיבור למארח אחר
137
+ ## Connecting a different host
134
138
 
135
- כל הערכה עובדת מול ממשק `MacroHost` אחד (פקודות, הכנסת טקסט, בחירה, החלפה, אירועי הקלדה). `createSuperdocHost` הוא המימוש ל-SuperDoc v2 במצב `ui: false`; עורך אחר מתחבר במימוש משלו של הממשקראו `src/types.ts` ואת הכפיל ב-`tests/fake-host.ts`.
139
+ The whole toolkit works against a single `MacroHost` interface (commands, text insertion, selection, replace, typing events). `createSuperdocHost` is the implementation for SuperDoc v2 in `ui: false` mode; another editor plugs in with its own implementation see `src/types.ts` and the double in `tests/fake-host.ts`.
136
140
 
137
- ## מגבלות ידועות
141
+ ## Known limitations
138
142
 
139
- - אין הרצת VBA. קובצי `.docm` נפתחים כרגיל אבל המאקרו שבהם אינו מורץ.
140
- - המקליט אינו מתעד תנועת סמן ובחירה בעכבר (כמו ב-Word — הניגון פועל מהסמן הנוכחי).
141
- - `deleteBackward` וטקסט-מלא-של-המסמך משתמשים ב-view הפנימי של המנוע (ProseMirror) — זמינים בדפדפן, לא ב-headless.
142
- - תקרת הזמן במריץ `eval` אינה עוצרת לולאה סינכרונית אינסופית (במריץ ה-iframe כן).
143
+ - No VBA execution. `.docm` files open normally but their macros are not run.
144
+ - The recorder does not capture caret movement or mouse selection (as in Word — replay acts from the current caret).
145
+ - `deleteBackward` and full-document text use the engine's internal view (ProseMirror) — available in the browser, not headless.
146
+ - The `eval` runner's time cap cannot stop an infinite synchronous loop (the iframe runner's can).
143
147
 
144
- ## פיתוח
148
+ ## Development
145
149
 
146
150
  ```bash
147
151
  npm install
148
- npm test # vitest — 43 בדיקות
149
- npm run build # tsc dist/
152
+ npm test # vitest
153
+ npm run build # tsc dist/
150
154
  ```
151
155
 
152
- **שחרור גרסה:** מעלים את `version` ב-package.json ודוחפים ל-main — ה-workflow
153
- (‎.github/workflows/release.yml) מפרסם ל-npm ויוצר GitHub Release אוטומטית.
156
+ **Releasing:** bump `version` in package.json and push to main — the workflow (.github/workflows/release.yml) publishes to npm and creates a GitHub Release automatically.
154
157
 
155
- ## רישיון
158
+ ## License
156
159
 
157
160
  MIT
@@ -1,22 +1,3 @@
1
- /**
2
- * מימוש `MacroHost` מעל SuperDoc v2 במצב מנוע-בלבד (`ui: false`) — הקונפיגורציה
3
- * של otzaria-word-editor.
4
- *
5
- * המשטחים שבשימוש, לפי סדר עדיפות:
6
- * 1. `superdoc.ui.commands` — קטלוג הפקודות של ה-controller (הרצה + תצפית).
7
- * 2. `superdoc.activeEditor.doc` — ה-Document API הציבורי (בחירה, הכנסה, בלוקים).
8
- * 3. `superdoc.ui.search` — חיפוש/החלפה.
9
- * 4. `superdoc.activeEditor.view` — מופע ProseMirror הפנימי, **רק** לפערים
10
- * שאין להם משטח ציבורי: מחיקה לאחור וטקסט מלא של המסמך. קיים בדפדפן
11
- * ו-null ב-headless.
12
- *
13
- * הטיפוסים כאן מבניים (structural) ואינם מייבאים מ-superdoc: הערכה לא תלויה
14
- * בחבילה, וגרסת מנוע שמשנה שדה תיכשל סגור (הפונקציה תחזיר כשל) ולא תקרוס.
15
- *
16
- * תצפית הפקודות למקליט נעשית בעטיפת `executeAsync` על אובייקט ה-commands.
17
- * זה מכסה כל מסלול שקורא לו — כולל ה-CommandAdapter של otzaria — בלי לשנות
18
- * את הקוד הקורא. `dispose()` מחזיר את המתודה המקורית.
19
- */
20
1
  import type { MacroHost } from '../types.js';
21
2
  interface CommandStateLike {
22
3
  reason?: string;
@@ -119,11 +100,11 @@ export interface SuperdocLike {
119
100
  }
120
101
  export interface SuperdocHostOptions {
121
102
  superdoc: SuperdocLike;
122
- /** האלמנט שהמסמך מרונדר בתוכועליו נקלטים אירועי ההקלדה. */
103
+ /** The element the document renders in typing events are captured on it. */
123
104
  container?: HTMLElement | null;
124
105
  }
125
106
  export interface SuperdocMacroHost extends MacroHost {
126
- /** מסירה את עטיפת התצפית ואת מאזיני ה-DOM. לקרוא לפני החלפת מסמך. */
107
+ /** Removes the observation wrapper and the DOM listeners. Call before swapping documents. */
127
108
  dispose(): void;
128
109
  }
129
110
  export declare function createSuperdocHost(options: SuperdocHostOptions): SuperdocMacroHost;
@@ -1,5 +1,29 @@
1
- /* ---------- עזרים ---------- */
2
- const NOT_READY = { ok: false, message: 'אין מסמך פתוח', reason: 'not-ready' };
1
+ /**
2
+ * The `MacroHost` implementation on top of SuperDoc v2 in engine-only mode
3
+ * (`ui: false`) — the configuration otzaria-word-editor runs.
4
+ *
5
+ * The surfaces used, in order of preference:
6
+ * 1. `superdoc.ui.commands` — the controller's command catalog (execution + observation).
7
+ * 2. `superdoc.activeEditor.doc` — the public Document API (selection, insertion, blocks).
8
+ * 3. `superdoc.ui.search` — find/replace.
9
+ * 4. `superdoc.activeEditor.view` — the internal ProseMirror instance,
10
+ * **only** for gaps that have no public surface: backward deletion and
11
+ * the document's full text. Present in the browser, null headless.
12
+ *
13
+ * The types here are structural and do not import from superdoc: the toolkit
14
+ * does not depend on the package, and an engine version that changes a field
15
+ * fails closed (the function returns a failure) rather than crashing.
16
+ *
17
+ * Command observation for the recorder wraps `executeAsync` on the commands
18
+ * object. That covers every path that calls it — including otzaria's
19
+ * CommandAdapter — without changing the calling code. `dispose()` restores
20
+ * the original method.
21
+ */
22
+ import { macroMessages } from '../messages.js';
23
+ /* ---------- Helpers ---------- */
24
+ function notReady() {
25
+ return { ok: false, message: macroMessages().noDocument, reason: 'not-ready' };
26
+ }
3
27
  function failed(message, reason) {
4
28
  return { ok: false, message, reason };
5
29
  }
@@ -16,17 +40,17 @@ function receiptOutcome(receipt, failedAction) {
16
40
  function emptySelection() {
17
41
  return { text: '', hasRange: false, blockId: null, selectionTarget: null, empty: true };
18
42
  }
19
- /* ---------- המימוש ---------- */
43
+ /* ---------- The implementation ---------- */
20
44
  export function createSuperdocHost(options) {
21
45
  const { superdoc, container } = options;
22
- // נקראים ברגע השימוש ולא נשמרים: activeEditor מוחלף בכל פתיחת מסמך.
46
+ // Read at call time, never cached: activeEditor is replaced on every document open.
23
47
  const commands = () => superdoc.ui?.commands ?? null;
24
48
  const doc = () => superdoc.activeEditor?.doc ?? null;
25
49
  const view = () => superdoc.activeEditor?.view ?? null;
26
50
  const search = () => superdoc.ui?.search ?? null;
27
51
  const commandListeners = new Set();
28
52
  const inputListeners = new Set();
29
- /* תצפית פקודות: עטיפת executeAsync, פעם אחת, עם שחזור ב-dispose. */
53
+ /* Command observation: wrap executeAsync, once, restored on dispose. */
30
54
  const wrapped = commands();
31
55
  const originalExecuteAsync = wrapped?.executeAsync;
32
56
  if (wrapped && originalExecuteAsync) {
@@ -36,13 +60,13 @@ export function createSuperdocHost(options) {
36
60
  listener(id, payload);
37
61
  }
38
62
  catch (error) {
39
- console.warn('[superdoc-macros] מאזין פקודות זרק', error);
63
+ console.warn('[superdoc-macros] command listener threw', error);
40
64
  }
41
65
  }
42
66
  return originalExecuteAsync.call(wrapped, id, payload);
43
67
  };
44
68
  }
45
- /* הקלדה: beforeinput על ה-container, בשלב הלכידה. */
69
+ /* Typing: beforeinput on the container, capture phase. */
46
70
  const onBeforeInput = (event) => {
47
71
  const input = event;
48
72
  let mapped = null;
@@ -72,7 +96,7 @@ export function createSuperdocHost(options) {
72
96
  listener(mapped);
73
97
  }
74
98
  catch (error) {
75
- console.warn('[superdoc-macros] מאזין הקלדה זרק', error);
99
+ console.warn('[superdoc-macros] input listener threw', error);
76
100
  }
77
101
  }
78
102
  };
@@ -119,23 +143,23 @@ export function createSuperdocHost(options) {
119
143
  async execute(id, payload) {
120
144
  const bus = commands();
121
145
  if (!bus)
122
- return NOT_READY;
146
+ return notReady();
123
147
  if (!bus.has(id))
124
- return failed(`הפקודה ${id} אינה מוכרת למנוע`, 'unknown-command');
148
+ return failed(macroMessages().unknownCommand(id), 'unknown-command');
125
149
  let result;
126
150
  try {
127
151
  result = await bus.executeAsync(id, payload);
128
152
  }
129
153
  catch (error) {
130
- return failed(error instanceof Error ? error.message : 'הפעולה נכשלה', 'threw');
154
+ return failed(error instanceof Error ? error.message : macroMessages().actionFailed, 'threw');
131
155
  }
132
- // false = ה-controller לא ניתב את הפקודה; מצב הפקד מסביר למה.
156
+ // false = the controller did not route the command; the command state explains why.
133
157
  if (result === false) {
134
158
  const reason = bus.get(id).getState().reason;
135
- return failed(reason ? `הפעולה נכשלה (${reason})` : 'הפעולה נכשלה', reason);
159
+ return failed(reason ? `${macroMessages().actionFailed} (${reason})` : macroMessages().actionFailed, reason);
136
160
  }
137
161
  if (typeof result === 'object' && result !== null) {
138
- return receiptOutcome(result, `הפקודה ${id} נכשלה`);
162
+ return receiptOutcome(result, macroMessages().commandFailed(id));
139
163
  }
140
164
  return { ok: true };
141
165
  },
@@ -143,7 +167,7 @@ export function createSuperdocHost(options) {
143
167
  async insertText(text) {
144
168
  const insert = doc()?.insert;
145
169
  if (typeof insert === 'function') {
146
- // בלי target ההכנסה נופלת לסוף המסמךלכן היעד נלקח מהבחירה החיה.
170
+ // Without a target the insertion falls to the end of the document so the target comes from the live selection.
147
171
  const snapshot = await readSelection(false);
148
172
  try {
149
173
  const receipt = await insert({
@@ -151,13 +175,13 @@ export function createSuperdocHost(options) {
151
175
  type: 'text',
152
176
  ...(snapshot.selectionTarget ? { target: snapshot.selectionTarget } : {}),
153
177
  });
154
- return receiptOutcome(receipt, 'הכנסת הטקסט נכשלה');
178
+ return receiptOutcome(receipt, macroMessages().insertTextFailed);
155
179
  }
156
180
  catch (error) {
157
- return failed(error instanceof Error ? error.message : 'הכנסת הטקסט נכשלה', 'threw');
181
+ return failed(error instanceof Error ? error.message : macroMessages().insertTextFailed, 'threw');
158
182
  }
159
183
  }
160
- // נפילה לאחור: ProseMirror ישיר, כשה-Document API אינו זמין.
184
+ // Fallback: direct ProseMirror, when the Document API is unavailable.
161
185
  const pm = view();
162
186
  if (pm) {
163
187
  try {
@@ -168,16 +192,16 @@ export function createSuperdocHost(options) {
168
192
  return { ok: true };
169
193
  }
170
194
  catch (error) {
171
- return failed(error instanceof Error ? error.message : 'הכנסת הטקסט נכשלה', 'threw');
195
+ return failed(error instanceof Error ? error.message : macroMessages().insertTextFailed, 'threw');
172
196
  }
173
197
  }
174
- return NOT_READY;
198
+ return notReady();
175
199
  },
176
200
  async deleteBackward(count) {
177
- // אין משטח ציבורי למחיקהזה השימוש המרכזי ב-escape hatch של ProseMirror.
201
+ // No public deletion surfacethis is the main use of the ProseMirror escape hatch.
178
202
  const pm = view();
179
203
  if (!pm)
180
- return failed('מחיקה אינה זמינה במסמך הזה', 'view-unavailable');
204
+ return failed(macroMessages().deletionUnavailable, 'view-unavailable');
181
205
  try {
182
206
  const { from } = pm.state.selection;
183
207
  const start = Math.max(0, from - Math.max(0, Math.trunc(count)));
@@ -189,7 +213,7 @@ export function createSuperdocHost(options) {
189
213
  return { ok: true };
190
214
  }
191
215
  catch (error) {
192
- return failed(error instanceof Error ? error.message : 'המחיקה נכשלה', 'threw');
216
+ return failed(error instanceof Error ? error.message : macroMessages().deleteFailed, 'threw');
193
217
  }
194
218
  },
195
219
  getSelection(options) {
@@ -198,24 +222,28 @@ export function createSuperdocHost(options) {
198
222
  async replaceAll(query, replacement) {
199
223
  const handle = search();
200
224
  if (!handle)
201
- return { ok: false, replaced: 0, message: 'החיפוש אינו זמין' };
225
+ return { ok: false, replaced: 0, message: macroMessages().searchUnavailable };
202
226
  try {
203
227
  handle.open?.();
204
228
  const slice = handle.search(query);
205
229
  if (slice?.available === false) {
206
- return { ok: false, replaced: 0, message: 'החיפוש אינו זמין במסמך הזה' };
230
+ return { ok: false, replaced: 0, message: macroMessages().searchUnavailableInDocument };
207
231
  }
208
232
  const total = typeof slice?.total === 'number' ? slice.total : 0;
209
233
  if (total === 0)
210
234
  return { ok: true, replaced: 0 };
211
235
  const result = await handle.replaceAll(replacement);
212
236
  if (result && result.ok === false) {
213
- return { ok: false, replaced: 0, message: `ההחלפה נכשלה${result.reason ? ` (${result.reason})` : ''}` };
237
+ return {
238
+ ok: false,
239
+ replaced: 0,
240
+ message: `${macroMessages().replaceFailed}${result.reason ? ` (${result.reason})` : ''}`,
241
+ };
214
242
  }
215
243
  return { ok: true, replaced: total };
216
244
  }
217
245
  catch (error) {
218
- return { ok: false, replaced: 0, message: error instanceof Error ? error.message : 'ההחלפה נכשלה' };
246
+ return { ok: false, replaced: 0, message: error instanceof Error ? error.message : macroMessages().replaceFailed };
219
247
  }
220
248
  finally {
221
249
  try {
@@ -223,7 +251,7 @@ export function createSuperdocHost(options) {
223
251
  handle.close?.();
224
252
  }
225
253
  catch {
226
- /* ניקוי בלבד */
254
+ /* cleanup only */
227
255
  }
228
256
  }
229
257
  },
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export type { MacroHost, MacroOutcome, MacroStep, RecordedMacro, SavedScript, SelectionSnapshot, Snippet, TextInputEvent, } from './types.js';
2
2
  export { MacroKit, type MacroKitOptions } from './manager.js';
3
+ export { ENGLISH_MESSAGES, HEBREW_MESSAGES, setMacroMessages, type MacroMessages, } from './messages.js';
3
4
  export { createSuperdocHost, type SuperdocHostOptions, type SuperdocLike, type SuperdocMacroHost } from './host/superdoc-host.js';
4
5
  export { createMacroApi, MacroError, type MacroApi, type MacroBridge, type ScriptSelection } from './scripting/macro-api.js';
5
6
  export { createEvalRunner } from './scripting/eval-runner.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { MacroKit } from './manager.js';
2
+ export { ENGLISH_MESSAGES, HEBREW_MESSAGES, setMacroMessages, } from './messages.js';
2
3
  export { createSuperdocHost } from './host/superdoc-host.js';
3
4
  export { createMacroApi, MacroError } from './scripting/macro-api.js';
4
5
  export { createEvalRunner } from './scripting/eval-runner.js';
package/dist/manager.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * `MacroKit` — הפאסדה שמארח מתקין פעם אחת ומקבל את שלוש היכולות מחווטות:
3
- * סקריפטים (עם ארגז חול), מקליט, וקטעי טקסט עם השלמה אוטומטית — פלוס שמירה,
4
- * ייבוא/ייצוא וקיצורי מקלדת.
2
+ * `MacroKit` — the facade a host installs once to get all three capabilities
3
+ * wired together: scripts (sandboxed), the recorder, and snippets with
4
+ * auto-text plus persistence, import/export and keyboard shortcuts.
5
5
  *
6
- * כלל בטיחות אחד נאכף כאן: אין ריצה בזמן הקלטה ואין שתי ריצות במקביל.
7
- * ניגון או סקריפט שרצים תוך כדי הקלטה היו מוקלטים בעצמם ומכפילים את עצמם
8
- * בניגון הבא.
6
+ * One safety rule is enforced here: no running while recording, and no two
7
+ * runs at once. A replay or script running during a recording would be
8
+ * recorded itself and duplicate itself on the next replay.
9
9
  */
10
10
  import { type MacroApiOptions } from './scripting/macro-api.js';
11
11
  import type { MacroRunner, MacroRunOptions, MacroRunResult } from './scripting/runner.js';
@@ -17,18 +17,19 @@ import { type MacroStorage } from './storage.js';
17
17
  import type { MacroHost, RecordedMacro, SavedScript, Snippet } from './types.js';
18
18
  export interface MacroKitOptions {
19
19
  host: MacroHost;
20
- /** ברירת מחדל: localStorage. */
20
+ /** Default: localStorage. */
21
21
  storage?: MacroStorage;
22
22
  /**
23
- * `'iframe'` (ברירת המחדל) מריץ סקריפטים בארגז חול; `'eval'` מריץ ישירות —
24
- * ראו את האזהרה ב-eval-runner. אפשר גם למסור מריץ מותאם.
23
+ * `'iframe'` (the default) runs scripts in a sandbox; `'eval'` runs them
24
+ * directly see the warning in eval-runner. A custom runner can also be
25
+ * passed.
25
26
  */
26
27
  runner?: MacroRunner | 'iframe' | 'eval';
27
- /** אפשרויות ריצה לסקריפטים (זמן, תקרת קריאות). */
28
+ /** Run options for scripts (time, call cap). */
28
29
  runOptions?: MacroRunOptions;
29
- /** אפשרויות ההשלמה האוטומטית. */
30
+ /** Auto-text options. */
30
31
  autoText?: Omit<AutoTextOptions, 'onExpand' | 'onError'> & AutoTextOptions;
31
- /** יומן ריצה של `api.log`. */
32
+ /** Run log for `api.log`. */
32
33
  onLog?: MacroApiOptions['onLog'];
33
34
  }
34
35
  export declare class MacroKit {
@@ -51,17 +52,17 @@ export declare class MacroKit {
51
52
  }): SavedScript;
52
53
  removeScript(id: string): void;
53
54
  runScript(id: string): Promise<MacroRunResult>;
54
- /** מריצה סקריפט שלא נשמרלמשל מתוך עורך המאקרו לפני שמירה. */
55
+ /** Runs an unsaved scripte.g. from the macro editor before saving. */
55
56
  runSource(source: string): Promise<MacroRunResult>;
56
57
  get isRecording(): boolean;
57
58
  get recordedStepCount(): number;
58
59
  startRecording(): void;
59
- /** עוצרת ושומרת. `null` כשלא הוקלט אף צעדאין מה לשמור. */
60
+ /** Stops and saves. `null` when no step was recorded there is nothing to save. */
60
61
  stopRecording(name: string, shortcut?: string): RecordedMacro | null;
61
62
  cancelRecording(): void;
62
63
  listRecordings(): readonly RecordedMacro[];
63
64
  removeRecording(id: string): void;
64
- /** עדכון שם או קיצור של הקלטה קיימת. `null` כשההקלטה לא נמצאה. */
65
+ /** Renames a recording or edits its shortcut. `null` when the recording was not found. */
65
66
  updateRecording(input: {
66
67
  id: string;
67
68
  name?: string;
@@ -81,20 +82,22 @@ export declare class MacroKit {
81
82
  ok: boolean;
82
83
  message?: string;
83
84
  }>;
84
- /** מפעילה השלמה אוטומטית (trigger + רווח). מחזירה פונקציית כיבוי. */
85
+ /** Enables auto-text (trigger + space). Returns a disable function. */
85
86
  enableAutoText(): () => void;
86
87
  disableAutoText(): void;
87
88
  /**
88
- * קושרת את הקיצורים של כל מה ששמור (סקריפטים, הקלטות, קטעים) ליעד — בדרך
89
- * כלל ה-container של העורך או `window`. הרשימה חיה: שמירה חדשה נקלטת בלי
90
- * לקשור מחדש. מחזירה פונקציית ניתוק.
89
+ * Binds the shortcuts of everything saved (scripts, recordings, snippets)
90
+ * to a target usually the editor container or `window`. The list is
91
+ * live: a new save is picked up without rebinding. Returns a dispose
92
+ * function.
91
93
  */
92
94
  attachShortcuts(target: ShortcutTarget): () => void;
93
95
  private currentBindings;
94
96
  exportState(): string;
95
97
  /**
96
- * ייבוא מ-JSON שיוצא ב-`exportState`. במיזוג (`merge: true`) פריט מיובא עם
97
- * `id` קיים מחליף את הקיים; בלי מיזוג המצב כולו מוחלף.
98
+ * Imports JSON produced by `exportState`. With `merge: true` an imported
99
+ * item with an existing `id` replaces it; without merge the whole state is
100
+ * replaced.
98
101
  */
99
102
  importState(json: string, options?: {
100
103
  merge?: boolean;