superdoc-macros 0.1.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/LICENSE +21 -0
- package/README.md +154 -0
- package/dist/host/superdoc-host.d.ts +130 -0
- package/dist/host/superdoc-host.js +257 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +10 -0
- package/dist/manager.d.ts +102 -0
- package/dist/manager.js +249 -0
- package/dist/recorder/recorder.d.ts +57 -0
- package/dist/recorder/recorder.js +141 -0
- package/dist/scripting/eval-runner.d.ts +16 -0
- package/dist/scripting/eval-runner.js +69 -0
- package/dist/scripting/iframe-runner.d.ts +41 -0
- package/dist/scripting/iframe-runner.js +150 -0
- package/dist/scripting/macro-api.d.ts +69 -0
- package/dist/scripting/macro-api.js +102 -0
- package/dist/scripting/runner.d.ts +21 -0
- package/dist/scripting/runner.js +2 -0
- package/dist/shortcuts.d.ts +39 -0
- package/dist/shortcuts.js +79 -0
- package/dist/snippets/autotext.d.ts +43 -0
- package/dist/snippets/autotext.js +106 -0
- package/dist/snippets/snippets.d.ts +26 -0
- package/dist/snippets/snippets.js +38 -0
- package/dist/storage.d.ts +24 -0
- package/dist/storage.js +65 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.js +9 -0
- package/package.json +44 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const VARIABLE_PATTERN = /\{\{\s*([\p{L}\p{N}_-]+)\s*\}\}/gu;
|
|
2
|
+
export function renderSnippet(text, context = {}) {
|
|
3
|
+
const now = context.now ?? new Date();
|
|
4
|
+
return text.replace(VARIABLE_PATTERN, (whole, rawName) => {
|
|
5
|
+
const name = rawName.toLowerCase();
|
|
6
|
+
switch (name) {
|
|
7
|
+
case 'date':
|
|
8
|
+
return now.toLocaleDateString('he-IL');
|
|
9
|
+
case 'time':
|
|
10
|
+
return now.toLocaleTimeString('he-IL', { hour: '2-digit', minute: '2-digit' });
|
|
11
|
+
case 'datetime':
|
|
12
|
+
return `${now.toLocaleDateString('he-IL')} ${now.toLocaleTimeString('he-IL', { hour: '2-digit', minute: '2-digit' })}`;
|
|
13
|
+
case 'selection':
|
|
14
|
+
return context.selectionText ?? '';
|
|
15
|
+
default: {
|
|
16
|
+
const value = context.variables?.[rawName] ?? context.variables?.[name];
|
|
17
|
+
return value ?? whole;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** האם הקטע משתמש ב-`{{selection}}` — ואז ההרחבה צריכה לקרוא את הבחירה. */
|
|
23
|
+
export function usesSelection(text) {
|
|
24
|
+
return /\{\{\s*selection\s*\}\}/iu.test(text);
|
|
25
|
+
}
|
|
26
|
+
/** מרחיבה קטע במיקום הסמן. */
|
|
27
|
+
export async function expandSnippet(host, snippet, options = {}) {
|
|
28
|
+
// הבחירה נקראת רק כשנחוצה: חילוץ טקסט הבחירה עולה בביצועים במנוע.
|
|
29
|
+
const selectionText = usesSelection(snippet.text)
|
|
30
|
+
? (await host.getSelection({ includeText: true })).text
|
|
31
|
+
: undefined;
|
|
32
|
+
const rendered = renderSnippet(snippet.text, {
|
|
33
|
+
variables: options.variables,
|
|
34
|
+
selectionText,
|
|
35
|
+
now: options.now,
|
|
36
|
+
});
|
|
37
|
+
return host.insertText(rendered);
|
|
38
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* שמירת המאקרו, ההקלטות והקטעים. ברירת המחדל: localStorage; הממשק ניתן
|
|
3
|
+
* להחלפה כדי שמארח יוכל לשמור בקובץ (למשל ב-workspace של תוסף אוצריא).
|
|
4
|
+
*/
|
|
5
|
+
import type { RecordedMacro, SavedScript, Snippet } from './types.js';
|
|
6
|
+
export interface PersistedMacroState {
|
|
7
|
+
version: 1;
|
|
8
|
+
scripts: SavedScript[];
|
|
9
|
+
recordings: RecordedMacro[];
|
|
10
|
+
snippets: Snippet[];
|
|
11
|
+
}
|
|
12
|
+
export interface MacroStorage {
|
|
13
|
+
/** `null` כשאין מצב שמור או כשהשמור אינו קריא. */
|
|
14
|
+
load(): PersistedMacroState | null;
|
|
15
|
+
save(state: PersistedMacroState): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function emptyState(): PersistedMacroState;
|
|
18
|
+
/** מפענחת מצב שמור. `null` על כל צורה לא צפויה — לא זורקת. */
|
|
19
|
+
export declare function parsePersistedState(json: string): PersistedMacroState | null;
|
|
20
|
+
export declare const DEFAULT_STORAGE_KEY = "superdoc-macros:v1";
|
|
21
|
+
/** localStorage עם הגנות: גישה חסומה או מלאה אינה מפילה את הערכה. */
|
|
22
|
+
export declare function createLocalStorage(key?: string, storage?: Pick<Storage, 'getItem' | 'setItem'>): MacroStorage;
|
|
23
|
+
/** אחסון בזיכרון — לבדיקות ולמצבים שבהם אין persistence. */
|
|
24
|
+
export declare function createMemoryStorage(): MacroStorage;
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export function emptyState() {
|
|
2
|
+
return { version: 1, scripts: [], recordings: [], snippets: [] };
|
|
3
|
+
}
|
|
4
|
+
function isValidState(value) {
|
|
5
|
+
if (typeof value !== 'object' || value === null)
|
|
6
|
+
return false;
|
|
7
|
+
const state = value;
|
|
8
|
+
return (state.version === 1 &&
|
|
9
|
+
Array.isArray(state.scripts) &&
|
|
10
|
+
Array.isArray(state.recordings) &&
|
|
11
|
+
Array.isArray(state.snippets));
|
|
12
|
+
}
|
|
13
|
+
/** מפענחת מצב שמור. `null` על כל צורה לא צפויה — לא זורקת. */
|
|
14
|
+
export function parsePersistedState(json) {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(json);
|
|
17
|
+
return isValidState(parsed) ? parsed : null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export const DEFAULT_STORAGE_KEY = 'superdoc-macros:v1';
|
|
24
|
+
/** localStorage עם הגנות: גישה חסומה או מלאה אינה מפילה את הערכה. */
|
|
25
|
+
export function createLocalStorage(key = DEFAULT_STORAGE_KEY, storage) {
|
|
26
|
+
const backing = () => {
|
|
27
|
+
if (storage)
|
|
28
|
+
return storage;
|
|
29
|
+
try {
|
|
30
|
+
return globalThis.localStorage ?? null;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
load() {
|
|
38
|
+
try {
|
|
39
|
+
const raw = backing()?.getItem(key);
|
|
40
|
+
return raw ? parsePersistedState(raw) : null;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
save(state) {
|
|
47
|
+
try {
|
|
48
|
+
backing()?.setItem(key, JSON.stringify(state));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.warn('[superdoc-macros] שמירת המאקרו נכשלה', error);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** אחסון בזיכרון — לבדיקות ולמצבים שבהם אין persistence. */
|
|
57
|
+
export function createMemoryStorage() {
|
|
58
|
+
let saved = null;
|
|
59
|
+
return {
|
|
60
|
+
load: () => (saved ? JSON.parse(JSON.stringify(saved)) : null),
|
|
61
|
+
save(state) {
|
|
62
|
+
saved = JSON.parse(JSON.stringify(state));
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* החוזים המשותפים של הערכה.
|
|
3
|
+
*
|
|
4
|
+
* `MacroHost` הוא נקודת החיבור היחידה לעורך: כל שלוש היכולות (סקריפטים,
|
|
5
|
+
* מקליט, קטעי טקסט) עובדות מולו ולא מול SuperDoc ישירות. כך אפשר לבדוק את
|
|
6
|
+
* הערכה עם כפיל בזיכרון, וכך מארח אחר (גרסת מנוע אחרת, עורך אחר) מתחבר
|
|
7
|
+
* במימוש אחד של הממשק הזה.
|
|
8
|
+
*/
|
|
9
|
+
/** תוצאת פעולה. אותה צורה כמו `CommandOutcome` של otzaria-word-editor. */
|
|
10
|
+
export type MacroOutcome = {
|
|
11
|
+
ok: true;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
message: string;
|
|
15
|
+
reason?: string;
|
|
16
|
+
};
|
|
17
|
+
/** תצלום הבחירה במסמך ברגע הקריאה. */
|
|
18
|
+
export interface SelectionSnapshot {
|
|
19
|
+
/** הטקסט המסומן. `''` כשאין בחירה או כשלא התבקש. */
|
|
20
|
+
text: string;
|
|
21
|
+
/** האם יש טווח מסומן ולא רק סמן. */
|
|
22
|
+
hasRange: boolean;
|
|
23
|
+
/** מזהה הפסקה שהבחירה מתחילה בה, או `null`. */
|
|
24
|
+
blockId: string | null;
|
|
25
|
+
/** היעד שפעולות כתיבה (`insert`) צורכות. אטום — נמסר חזרה למנוע כמו שהוא. */
|
|
26
|
+
selectionTarget: unknown | null;
|
|
27
|
+
/** האם הבחירה ריקה (סמן בלבד). */
|
|
28
|
+
empty: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** אירוע הקלדה שהמארח מדווח למקליט ולהשלמה האוטומטית. */
|
|
31
|
+
export type TextInputEvent = {
|
|
32
|
+
kind: 'insert-text';
|
|
33
|
+
text: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'insert-paragraph';
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'delete-backward';
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'delete-forward';
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* מה שהערכה צריכה מהעורך. מימוש ל-SuperDoc v2 נמצא ב-`createSuperdocHost`;
|
|
43
|
+
* לבדיקות יש כפיל בזיכרון.
|
|
44
|
+
*/
|
|
45
|
+
export interface MacroHost {
|
|
46
|
+
commands: {
|
|
47
|
+
/** האם המנוע מכיר את הפקודה. */
|
|
48
|
+
has(id: string): boolean;
|
|
49
|
+
/** מריצה פקודה מהקטלוג של המנוע ומחזירה תוצאה מנורמלת. */
|
|
50
|
+
execute(id: string, payload?: unknown): Promise<MacroOutcome>;
|
|
51
|
+
/** מזהי הפקודות המוכרות, אם המארח יודע למנות אותם. */
|
|
52
|
+
ids(): readonly string[];
|
|
53
|
+
};
|
|
54
|
+
/** מכניסה טקסט במיקום הסמן (או בסוף המסמך כשאין סמן). */
|
|
55
|
+
insertText(text: string): Promise<MacroOutcome>;
|
|
56
|
+
/** מוחקת תווים לאחור מהסמן. */
|
|
57
|
+
deleteBackward(count: number): Promise<MacroOutcome>;
|
|
58
|
+
/** תצלום הבחירה הנוכחית. לעולם לא זורקת. */
|
|
59
|
+
getSelection(options?: {
|
|
60
|
+
includeText?: boolean;
|
|
61
|
+
}): Promise<SelectionSnapshot>;
|
|
62
|
+
/** מחליפה את כל המופעים של `query` ב-`replacement`. מחזירה כמה הוחלפו. */
|
|
63
|
+
replaceAll(query: string, replacement: string): Promise<{
|
|
64
|
+
ok: boolean;
|
|
65
|
+
replaced: number;
|
|
66
|
+
message?: string;
|
|
67
|
+
}>;
|
|
68
|
+
/** הטקסט המלא של גוף המסמך. `''` כשאינו זמין. */
|
|
69
|
+
getDocumentText(): Promise<string>;
|
|
70
|
+
/** מאזינה לכל פקודה שהמנוע מריץ (מכל מקור). מחזירה פונקציית ביטול. */
|
|
71
|
+
onCommand(listener: (id: string, payload: unknown) => void): () => void;
|
|
72
|
+
/** מאזינה להקלדה במסמך. מחזירה פונקציית ביטול. */
|
|
73
|
+
onTextInput(listener: (event: TextInputEvent) => void): () => void;
|
|
74
|
+
}
|
|
75
|
+
/** צעד אחד במאקרו מוקלט. JSON-serializable במלואו. */
|
|
76
|
+
export type MacroStep = {
|
|
77
|
+
type: 'command';
|
|
78
|
+
id: string;
|
|
79
|
+
payload?: unknown;
|
|
80
|
+
} | {
|
|
81
|
+
type: 'insert-text';
|
|
82
|
+
text: string;
|
|
83
|
+
} | {
|
|
84
|
+
type: 'insert-paragraph';
|
|
85
|
+
} | {
|
|
86
|
+
type: 'delete-backward';
|
|
87
|
+
count: number;
|
|
88
|
+
} | {
|
|
89
|
+
type: 'delete-forward';
|
|
90
|
+
count: number;
|
|
91
|
+
};
|
|
92
|
+
/** מאקרו מוקלט, כפי שהוא נשמר ומיובא/מיוצא. */
|
|
93
|
+
export interface RecordedMacro {
|
|
94
|
+
version: 1;
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
/** ISO-8601. */
|
|
98
|
+
createdAt?: string;
|
|
99
|
+
shortcut?: string;
|
|
100
|
+
steps: MacroStep[];
|
|
101
|
+
}
|
|
102
|
+
/** מאקרו כתוב — סקריפט JavaScript שרץ מול ה-API של הערכה. */
|
|
103
|
+
export interface SavedScript {
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
source: string;
|
|
107
|
+
shortcut?: string;
|
|
108
|
+
}
|
|
109
|
+
/** קטע טקסט (Snippet / AutoText). */
|
|
110
|
+
export interface Snippet {
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
/** תוכן הקטע. תומך במשתני `{{...}}` — ראו `renderSnippet`. */
|
|
114
|
+
text: string;
|
|
115
|
+
/** מילת הפעלה להשלמה אוטומטית: הקלדת המילה ואחריה רווח מחליפה אותה בתוכן. */
|
|
116
|
+
trigger?: string;
|
|
117
|
+
/** קיצור מקלדת, למשל `Ctrl+Alt+1`. */
|
|
118
|
+
shortcut?: string;
|
|
119
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* החוזים המשותפים של הערכה.
|
|
3
|
+
*
|
|
4
|
+
* `MacroHost` הוא נקודת החיבור היחידה לעורך: כל שלוש היכולות (סקריפטים,
|
|
5
|
+
* מקליט, קטעי טקסט) עובדות מולו ולא מול SuperDoc ישירות. כך אפשר לבדוק את
|
|
6
|
+
* הערכה עם כפיל בזיכרון, וכך מארח אחר (גרסת מנוע אחרת, עורך אחר) מתחבר
|
|
7
|
+
* במימוש אחד של הממשק הזה.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "superdoc-macros",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Macro toolkit for SuperDoc-based editors: sandboxed scripted macros, a Word-style macro recorder, and snippets with auto-text expansion.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"prepare": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/palmoni5/superdoc-macros.git"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"superdoc",
|
|
32
|
+
"macros",
|
|
33
|
+
"macro-recorder",
|
|
34
|
+
"snippets",
|
|
35
|
+
"autotext",
|
|
36
|
+
"docx",
|
|
37
|
+
"editor",
|
|
38
|
+
"otzaria"
|
|
39
|
+
],
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.7.2",
|
|
42
|
+
"vitest": "^3.2.4"
|
|
43
|
+
}
|
|
44
|
+
}
|