superdoc-macros 0.2.0 → 0.4.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,113 @@ 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`) |
76
+
77
+ ### Security
83
78
 
84
- ### אבטחה
79
+ The default is a **real sandbox**: scripts run in an iframe with `sandbox="allow-scripts"` only — an opaque origin, no access to the application's DOM, localStorage or cookies — plus a `default-src 'none'` CSP inside the iframe, so the script cannot fetch or open sockets to the public internet either. The script's only way to touch the document is the API above, with a time cap (default 30 s — enforced even against infinite loops, by removing the iframe) and a call cap (10,000). When a run ends (result, error or timeout), its bridge is revoked: any late call is rejected and can no longer touch the document.
85
80
 
86
- ברירת המחדל היא **ארגז חול אמיתי**: הסקריפט רץ ב-iframe עם `sandbox="allow-scripts"` בלבד — origin אטום, בלי גישה ל-DOM של האפליקציה, ל-localStorage, ל-cookies או לרשת עם אישורי המשתמש. הדרך היחידה שלו לגעת במסמך היא ה-API שלמעלה, עם תקרת זמן (ברירת מחדל 30 שניות נאכפת גם על לולאה אינסופית, ע"י הסרת ה-iframe) ותקרת קריאות (10,000).
81
+ Honest limits: the browser offers no per-iframe memory cap, and a host call that already reached the engine cannot be aborted mid-flight (the engine exposes no cancellation)what is guaranteed is that nothing new starts.
87
82
 
88
- מי שחייב לוותר על הבידוד (למשל CSP שחוסם `srcdoc`) יכול לעבור למריץ ישיר: `new MacroKit({ host, runner: 'eval' })` — ראו האזהרות בקוד.
83
+ 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
84
 
90
- ## 2. מקליט מאקרו
85
+ ## 2. Macro recorder
91
86
 
92
87
  ```ts
93
88
  kit.startRecording();
94
- // המשתמש עובד רגיל: מקליד, מדגיש, ממספר...
95
- const recording = kit.stopRecording('פתיח סטנדרטי', 'Ctrl+Alt+1');
89
+ // the user works normally: typing, formatting, lists...
90
+ const recording = kit.stopRecording('Standard intro', 'Ctrl+Alt+1');
96
91
 
97
- // מאוחר יותר, מכל מקום במסמך:
92
+ // later, from anywhere in the document:
98
93
  await kit.replayRecording(recording.id);
99
94
  ```
100
95
 
101
- המקליט מתעד **פקודות והקלדה**, לא מיקומי סמן בדיוק כמו המקליט של Word: הניגון חל במקום שבו הסמן עומד. הקשות רצופות מתלכדות לצעד אחד, `undo`/`redo` אינם מוקלטים (ניתן לשינוי ב-`RecorderOptions`), וההקלטה נשמרת כ-JSON נקי שאפשר לייצא ולשתף.
96
+ 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
97
 
103
- ## 3. קטעי טקסט והשלמה אוטומטית
98
+ ## 3. Snippets and auto-text
104
99
 
105
100
  ```ts
106
- kit.saveSnippet({ name: 'בס"ד', text: 'בס"ד', trigger: 'בסד' });
101
+ kit.saveSnippet({ name: 'BSD', text: 'בס"ד', trigger: 'בסד' });
107
102
  kit.saveSnippet({
108
- name: 'חתימה',
109
- text: 'ונשלם בעז"ה, {{date}}',
103
+ name: 'Signature',
104
+ text: 'Best regards, {{date}}',
110
105
  shortcut: 'Ctrl+Alt+S',
111
106
  });
112
- kit.saveSnippet({ name: 'ציטוט', text: '(עיין {{selection}})' });
107
+ kit.saveSnippet({ name: 'Citation', text: '(see {{selection}})' });
113
108
 
114
- kit.enableAutoText(); // מעכשיו: הקלדת "בסד" + רווח בס"ד
115
- await kit.expandSnippet(id); // או הרחבה יזומה / דרך הקיצור
109
+ kit.enableAutoText(); // from now on: typing the trigger + space expands it
110
+ await kit.expandSnippet(id); // or expand explicitly / via the shortcut
116
111
  ```
117
112
 
118
- משתנים מובנים: `{{date}}`, `{{time}}`, `{{datetime}}` (עברית), `{{selection}}`. כל שם אחר נפתר מ-`variables` שנמסרו ל-`expandSnippet`; משתנה ללא ערך נשאר גלוי בטקסט.
113
+ 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.
119
114
 
120
- ## שמירה, ייבוא וייצוא
115
+ ## Localization
116
+
117
+ Runtime messages (failures shown to end users) default to English. A host with a localized UI swaps them once at startup:
118
+
119
+ ```ts
120
+ import { setMacroMessages, HEBREW_MESSAGES } from 'superdoc-macros';
121
+
122
+ setMacroMessages(HEBREW_MESSAGES); // full Hebrew locale (included)
123
+ setMacroMessages({ scriptNotFound: '…' }); // or a partial override
124
+ ```
125
+
126
+ ## Persistence, import and export
121
127
 
122
128
  ```ts
123
129
  import { createLocalStorage } from 'superdoc-macros';
124
130
 
125
131
  const kit = new MacroKit({ host, storage: createLocalStorage('my-key') });
126
132
 
127
- const json = kit.exportState(); // גיבוי / שיתוף
133
+ const json = kit.exportState(); // backup / sharing
128
134
  kit.importState(json, { merge: true });
129
135
  ```
130
136
 
131
- `MacroStorage` הוא ממשק בן שתי מתודות אפשר לממש שמירה לקובץ (למשל ב-workspace של תוסף אוצריא).
137
+ `MacroStorage` is a two-method interfaceimplement it to persist to a file (e.g. a plugin workspace).
138
+
139
+ Imports are strictly validated: every item and every recorded step is type-checked and size-bounded (see `IMPORT_LIMITS`), and one invalid item rejects the whole file — no partial imports.
140
+
141
+ ## Shortcut safety
142
+
143
+ Saved bindings go through `kit.validateShortcut(shortcut, excludeId?)` — enforced on every save: a binding must parse, must carry a real modifier (Ctrl/Alt/Meta — a bare letter would fire on ordinary typing), must not collide with another saved item, and must not collide with shortcuts the host declared as reserved:
144
+
145
+ ```ts
146
+ const kit = new MacroKit({ host, reservedShortcuts: ['Ctrl+S', 'Ctrl+P', /* … the editor's registry … */] });
147
+ ```
132
148
 
133
- ## חיבור למארח אחר
149
+ ## Connecting a different host
134
150
 
135
- כל הערכה עובדת מול ממשק `MacroHost` אחד (פקודות, הכנסת טקסט, בחירה, החלפה, אירועי הקלדה). `createSuperdocHost` הוא המימוש ל-SuperDoc v2 במצב `ui: false`; עורך אחר מתחבר במימוש משלו של הממשקראו `src/types.ts` ואת הכפיל ב-`tests/fake-host.ts`.
151
+ 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
152
 
137
- ## מגבלות ידועות
153
+ ## Known limitations
138
154
 
139
- - אין הרצת VBA. קובצי `.docm` נפתחים כרגיל אבל המאקרו שבהם אינו מורץ.
140
- - המקליט אינו מתעד תנועת סמן ובחירה בעכבר (כמו ב-Word — הניגון פועל מהסמן הנוכחי).
141
- - `deleteBackward` וטקסט-מלא-של-המסמך משתמשים ב-view הפנימי של המנוע (ProseMirror) — זמינים בדפדפן, לא ב-headless.
142
- - תקרת הזמן במריץ `eval` אינה עוצרת לולאה סינכרונית אינסופית (במריץ ה-iframe כן).
155
+ - No VBA execution. `.docm` files open normally but their macros are not run.
156
+ - The recorder does not capture caret movement or mouse selection (as in Word — replay acts from the current caret).
157
+ - `deleteBackward` and full-document text use the engine's internal view (ProseMirror) — available in the browser, not headless.
158
+ - The `eval` runner's time cap cannot stop an infinite synchronous loop (the iframe runner's can).
143
159
 
144
- ## פיתוח
160
+ ## Development
145
161
 
146
162
  ```bash
147
163
  npm install
148
- npm test # vitest — 43 בדיקות
149
- npm run build # tsc dist/
164
+ npm test # vitest
165
+ npm run build # tsc dist/
150
166
  ```
151
167
 
152
- **שחרור גרסה:** מעלים את `version` ב-package.json ודוחפים ל-main — ה-workflow
153
- (‎.github/workflows/release.yml) מפרסם ל-npm ויוצר GitHub Release אוטומטית.
168
+ **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
169
 
155
- ## רישיון
170
+ ## License
156
171
 
157
172
  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,19 @@ 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;
105
+ /**
106
+ * Whether the adapter may fall back to the engine's internal ProseMirror
107
+ * view for the operations that have no public surface: backward/forward
108
+ * deletion, full-document text, and insertion when the Document API is
109
+ * missing. Default: true. Hosts that want to stay strictly on public
110
+ * surfaces set false — those operations then fail closed.
111
+ */
112
+ viewFallback?: boolean;
124
113
  }
125
114
  export interface SuperdocMacroHost extends MacroHost {
126
- /** מסירה את עטיפת התצפית ואת מאזיני ה-DOM. לקרוא לפני החלפת מסמך. */
115
+ /** Removes the observation wrapper and the DOM listeners. Call before swapping documents. */
127
116
  dispose(): void;
128
117
  }
129
118
  export declare function createSuperdocHost(options: SuperdocHostOptions): SuperdocMacroHost;