roam-research-mcp 1.4.0 → 2.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 +360 -31
- package/build/Roam_Markdown_Cheatsheet.md +30 -12
- package/build/cli/batch/resolver.js +138 -0
- package/build/cli/batch/translator.js +363 -0
- package/build/cli/batch/types.js +4 -0
- package/build/cli/commands/batch.js +352 -0
- package/build/cli/commands/get.js +161 -0
- package/build/cli/commands/refs.js +135 -0
- package/build/cli/commands/rename.js +58 -0
- package/build/cli/commands/save.js +498 -0
- package/build/cli/commands/search.js +240 -0
- package/build/cli/commands/status.js +91 -0
- package/build/cli/commands/update.js +151 -0
- package/build/cli/roam.js +35 -0
- package/build/cli/utils/graph.js +56 -0
- package/build/cli/utils/output.js +122 -0
- package/build/config/environment.js +70 -34
- package/build/config/graph-registry.js +221 -0
- package/build/config/graph-registry.test.js +30 -0
- package/build/search/block-ref-search.js +34 -7
- package/build/search/status-search.js +5 -4
- package/build/server/roam-server.js +98 -53
- package/build/shared/validation.js +10 -5
- package/build/tools/helpers/refs.js +50 -31
- package/build/tools/operations/blocks.js +38 -1
- package/build/tools/operations/memory.js +51 -5
- package/build/tools/operations/pages.js +186 -111
- package/build/tools/operations/search/index.js +5 -1
- package/build/tools/operations/todos.js +1 -1
- package/build/tools/schemas.js +121 -41
- package/build/tools/tool-handlers.js +9 -2
- package/build/utils/helpers.js +22 -0
- package/package.json +11 -7
- package/build/cli/import-markdown.js +0 -98
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolver for page/block title lookups before batch execution
|
|
3
|
+
*/
|
|
4
|
+
import { q } from '@roam-research/roam-api-sdk';
|
|
5
|
+
import { capitalizeWords } from '../../tools/helpers/text.js';
|
|
6
|
+
import { formatRoamDate } from '../../utils/helpers.js';
|
|
7
|
+
import { pageUidCache } from '../../cache/page-uid-cache.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a page title to its UID, trying multiple case variations
|
|
10
|
+
*/
|
|
11
|
+
export async function resolvePageUid(graph, title) {
|
|
12
|
+
// Check cache first
|
|
13
|
+
const cachedUid = pageUidCache.get(title);
|
|
14
|
+
if (cachedUid) {
|
|
15
|
+
return cachedUid;
|
|
16
|
+
}
|
|
17
|
+
// Try different case variations
|
|
18
|
+
const variations = [
|
|
19
|
+
title,
|
|
20
|
+
capitalizeWords(title),
|
|
21
|
+
title.toLowerCase()
|
|
22
|
+
];
|
|
23
|
+
const orClause = variations.map(v => `[?e :node/title "${v}"]`).join(' ');
|
|
24
|
+
const searchQuery = `[:find ?uid .
|
|
25
|
+
:where [?e :block/uid ?uid]
|
|
26
|
+
(or ${orClause})]`;
|
|
27
|
+
const result = await q(graph, searchQuery, []);
|
|
28
|
+
const uid = (result === null || result === undefined) ? null : String(result);
|
|
29
|
+
// Cache the result
|
|
30
|
+
if (uid) {
|
|
31
|
+
pageUidCache.set(title, uid);
|
|
32
|
+
}
|
|
33
|
+
return uid;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get today's daily page title in Roam format
|
|
37
|
+
*/
|
|
38
|
+
export function getDailyPageTitle() {
|
|
39
|
+
return formatRoamDate(new Date());
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve today's daily page UID
|
|
43
|
+
*/
|
|
44
|
+
export async function resolveDailyPageUid(graph) {
|
|
45
|
+
const dailyTitle = getDailyPageTitle();
|
|
46
|
+
return resolvePageUid(graph, dailyTitle);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Collect all unique page titles that need resolution from commands
|
|
50
|
+
*/
|
|
51
|
+
export function collectPageTitles(commands) {
|
|
52
|
+
const titles = new Set();
|
|
53
|
+
for (const cmd of commands) {
|
|
54
|
+
const params = cmd.params;
|
|
55
|
+
// Commands that can have 'page' param
|
|
56
|
+
if ('page' in params && typeof params.page === 'string') {
|
|
57
|
+
titles.add(params.page);
|
|
58
|
+
}
|
|
59
|
+
// Remember command can have heading that needs parent page resolution
|
|
60
|
+
// But heading lookup is handled separately
|
|
61
|
+
// Todo/remember without explicit page need daily page
|
|
62
|
+
if (cmd.command === 'todo' || cmd.command === 'remember') {
|
|
63
|
+
if (!('page' in params) && !('pageUid' in params) && !('parent' in params)) {
|
|
64
|
+
titles.add(getDailyPageTitle());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return titles;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if any commands need daily page resolution
|
|
72
|
+
*/
|
|
73
|
+
export function needsDailyPage(commands) {
|
|
74
|
+
for (const cmd of commands) {
|
|
75
|
+
const params = cmd.params;
|
|
76
|
+
if (cmd.command === 'todo' || cmd.command === 'remember') {
|
|
77
|
+
if (!('page' in params) && !('pageUid' in params) && !('parent' in params)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve all page titles to UIDs
|
|
86
|
+
* Returns a map of title -> uid
|
|
87
|
+
*/
|
|
88
|
+
export async function resolveAllPages(graph, titles) {
|
|
89
|
+
const resolved = new Map();
|
|
90
|
+
// Resolve in parallel for efficiency
|
|
91
|
+
const entries = Array.from(titles);
|
|
92
|
+
const results = await Promise.all(entries.map(async (title) => {
|
|
93
|
+
const uid = await resolvePageUid(graph, title);
|
|
94
|
+
return [title, uid];
|
|
95
|
+
}));
|
|
96
|
+
for (const [title, uid] of results) {
|
|
97
|
+
if (uid) {
|
|
98
|
+
resolved.set(title, uid);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create initial resolution context
|
|
105
|
+
*/
|
|
106
|
+
export function createResolutionContext() {
|
|
107
|
+
return {
|
|
108
|
+
pageUids: new Map(),
|
|
109
|
+
placeholders: new Map(),
|
|
110
|
+
levelStack: [],
|
|
111
|
+
currentParent: null,
|
|
112
|
+
dailyPageUid: null
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a parent reference - could be a UID, placeholder, or page title
|
|
117
|
+
*/
|
|
118
|
+
export function resolveParentRef(ref, context) {
|
|
119
|
+
// Check if it's a placeholder reference {{name}}
|
|
120
|
+
const placeholderMatch = ref.match(/^\{\{([^}]+)\}\}$/);
|
|
121
|
+
if (placeholderMatch) {
|
|
122
|
+
const name = placeholderMatch[1];
|
|
123
|
+
return context.placeholders.get(name) || `{{uid:${name}}}`;
|
|
124
|
+
}
|
|
125
|
+
// Check if it's a resolved page title
|
|
126
|
+
if (context.pageUids.has(ref)) {
|
|
127
|
+
return context.pageUids.get(ref);
|
|
128
|
+
}
|
|
129
|
+
// Assume it's a direct UID
|
|
130
|
+
return ref;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generate a placeholder UID for tracking
|
|
134
|
+
* Returns the placeholder in {{uid:name}} format for batch processing
|
|
135
|
+
*/
|
|
136
|
+
export function generatePlaceholder(name) {
|
|
137
|
+
return `{{uid:${name}}}`;
|
|
138
|
+
}
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Translator: converts CLI-style commands to Roam batch actions
|
|
3
|
+
*/
|
|
4
|
+
import { resolveParentRef, generatePlaceholder } from './resolver.js';
|
|
5
|
+
/**
|
|
6
|
+
* Translate a single command to batch actions
|
|
7
|
+
* May return multiple actions (e.g., table, outline)
|
|
8
|
+
*/
|
|
9
|
+
export function translateCommand(command, context) {
|
|
10
|
+
switch (command.command) {
|
|
11
|
+
case 'create':
|
|
12
|
+
return translateCreate(command, context);
|
|
13
|
+
case 'update':
|
|
14
|
+
return translateUpdate(command);
|
|
15
|
+
case 'delete':
|
|
16
|
+
return translateDelete(command);
|
|
17
|
+
case 'move':
|
|
18
|
+
return translateMove(command);
|
|
19
|
+
case 'todo':
|
|
20
|
+
return translateTodo(command, context);
|
|
21
|
+
case 'table':
|
|
22
|
+
return translateTable(command, context);
|
|
23
|
+
case 'outline':
|
|
24
|
+
return translateOutline(command, context);
|
|
25
|
+
case 'remember':
|
|
26
|
+
return translateRemember(command, context);
|
|
27
|
+
case 'page':
|
|
28
|
+
return translatePage(command, context);
|
|
29
|
+
case 'codeblock':
|
|
30
|
+
return translateCodeblock(command, context);
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`Unknown command: ${command.command}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve parent UID from command params
|
|
37
|
+
*/
|
|
38
|
+
function getParentUid(params, context) {
|
|
39
|
+
// Direct parent UID or placeholder
|
|
40
|
+
if (params.parent) {
|
|
41
|
+
return resolveParentRef(params.parent, context) || params.parent;
|
|
42
|
+
}
|
|
43
|
+
// Page UID
|
|
44
|
+
if (params.pageUid) {
|
|
45
|
+
return params.pageUid;
|
|
46
|
+
}
|
|
47
|
+
// Page title -> resolved UID
|
|
48
|
+
if (params.page) {
|
|
49
|
+
const uid = context.pageUids.get(params.page);
|
|
50
|
+
if (!uid) {
|
|
51
|
+
throw new Error(`Page "${params.page}" not found`);
|
|
52
|
+
}
|
|
53
|
+
return uid;
|
|
54
|
+
}
|
|
55
|
+
// Default to daily page
|
|
56
|
+
if (context.dailyPageUid) {
|
|
57
|
+
return context.dailyPageUid;
|
|
58
|
+
}
|
|
59
|
+
throw new Error('No parent specified and daily page not resolved');
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Register a placeholder in the context
|
|
63
|
+
*/
|
|
64
|
+
function registerPlaceholder(name, context) {
|
|
65
|
+
const placeholder = generatePlaceholder(name);
|
|
66
|
+
context.placeholders.set(name, placeholder);
|
|
67
|
+
return placeholder;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Update level stack for hierarchy tracking
|
|
71
|
+
*/
|
|
72
|
+
function updateLevelStack(level, uid, context) {
|
|
73
|
+
// Ensure stack is long enough
|
|
74
|
+
while (context.levelStack.length < level) {
|
|
75
|
+
context.levelStack.push('');
|
|
76
|
+
}
|
|
77
|
+
context.levelStack[level - 1] = uid;
|
|
78
|
+
// Truncate stack above current level
|
|
79
|
+
context.levelStack.length = level;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get parent from level stack for hierarchical nesting
|
|
83
|
+
*/
|
|
84
|
+
function getParentFromLevel(level, context) {
|
|
85
|
+
if (level <= 1) {
|
|
86
|
+
return context.currentParent;
|
|
87
|
+
}
|
|
88
|
+
const parentLevel = level - 1;
|
|
89
|
+
if (parentLevel <= context.levelStack.length && context.levelStack[parentLevel - 1]) {
|
|
90
|
+
return context.levelStack[parentLevel - 1];
|
|
91
|
+
}
|
|
92
|
+
return context.currentParent;
|
|
93
|
+
}
|
|
94
|
+
// --- Command translators ---
|
|
95
|
+
function translateCreate(cmd, context) {
|
|
96
|
+
const { params } = cmd;
|
|
97
|
+
let parentUid;
|
|
98
|
+
// Handle level-based hierarchy
|
|
99
|
+
if (params.level !== undefined && params.level > 1) {
|
|
100
|
+
const levelParent = getParentFromLevel(params.level, context);
|
|
101
|
+
if (levelParent) {
|
|
102
|
+
parentUid = levelParent;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
parentUid = getParentUid(params, context);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
parentUid = getParentUid(params, context);
|
|
110
|
+
// Set as current parent for level-based children
|
|
111
|
+
if (params.level === 1 || params.level === undefined) {
|
|
112
|
+
context.currentParent = parentUid;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const action = {
|
|
116
|
+
action: 'create-block',
|
|
117
|
+
string: params.text,
|
|
118
|
+
location: {
|
|
119
|
+
'parent-uid': parentUid,
|
|
120
|
+
order: params.order ?? 'last'
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
// Register placeholder if 'as' is specified
|
|
124
|
+
if (params.as) {
|
|
125
|
+
action.uid = registerPlaceholder(params.as, context);
|
|
126
|
+
}
|
|
127
|
+
// Optional properties
|
|
128
|
+
if (params.heading) {
|
|
129
|
+
action.heading = params.heading;
|
|
130
|
+
}
|
|
131
|
+
if (params['children-view-type']) {
|
|
132
|
+
action['children-view-type'] = params['children-view-type'];
|
|
133
|
+
}
|
|
134
|
+
// Update level stack if level is specified
|
|
135
|
+
if (params.level !== undefined && params.as) {
|
|
136
|
+
updateLevelStack(params.level, `{{uid:${params.as}}}`, context);
|
|
137
|
+
}
|
|
138
|
+
return [action];
|
|
139
|
+
}
|
|
140
|
+
function translateUpdate(cmd) {
|
|
141
|
+
const { params } = cmd;
|
|
142
|
+
const action = {
|
|
143
|
+
action: 'update-block',
|
|
144
|
+
uid: params.uid,
|
|
145
|
+
string: params.text
|
|
146
|
+
};
|
|
147
|
+
if (params.heading !== undefined) {
|
|
148
|
+
action.heading = params.heading;
|
|
149
|
+
}
|
|
150
|
+
if (params.open !== undefined) {
|
|
151
|
+
action.open = params.open;
|
|
152
|
+
}
|
|
153
|
+
if (params['text-align']) {
|
|
154
|
+
action['text-align'] = params['text-align'];
|
|
155
|
+
}
|
|
156
|
+
if (params['children-view-type']) {
|
|
157
|
+
action['children-view-type'] = params['children-view-type'];
|
|
158
|
+
}
|
|
159
|
+
return [action];
|
|
160
|
+
}
|
|
161
|
+
function translateDelete(cmd) {
|
|
162
|
+
return [{
|
|
163
|
+
action: 'delete-block',
|
|
164
|
+
uid: cmd.params.uid
|
|
165
|
+
}];
|
|
166
|
+
}
|
|
167
|
+
function translateMove(cmd) {
|
|
168
|
+
const { params } = cmd;
|
|
169
|
+
return [{
|
|
170
|
+
action: 'move-block',
|
|
171
|
+
uid: params.uid,
|
|
172
|
+
location: {
|
|
173
|
+
'parent-uid': params.parent,
|
|
174
|
+
order: params.order ?? 'last'
|
|
175
|
+
}
|
|
176
|
+
}];
|
|
177
|
+
}
|
|
178
|
+
function translateTodo(cmd, context) {
|
|
179
|
+
const { params } = cmd;
|
|
180
|
+
const parentUid = getParentUid(params, context);
|
|
181
|
+
const action = {
|
|
182
|
+
action: 'create-block',
|
|
183
|
+
string: `{{[[TODO]]}} ${params.text}`,
|
|
184
|
+
location: {
|
|
185
|
+
'parent-uid': parentUid,
|
|
186
|
+
order: params.order ?? 'last'
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
if (params.as) {
|
|
190
|
+
action.uid = registerPlaceholder(params.as, context);
|
|
191
|
+
}
|
|
192
|
+
return [action];
|
|
193
|
+
}
|
|
194
|
+
function translateTable(cmd, context) {
|
|
195
|
+
const { params } = cmd;
|
|
196
|
+
const parentUid = getParentUid(params, context);
|
|
197
|
+
const actions = [];
|
|
198
|
+
// Table container
|
|
199
|
+
const tableContainerPlaceholder = params.as
|
|
200
|
+
? registerPlaceholder(params.as, context)
|
|
201
|
+
: registerPlaceholder(`_table_${Date.now()}`, context);
|
|
202
|
+
actions.push({
|
|
203
|
+
action: 'create-block',
|
|
204
|
+
uid: tableContainerPlaceholder,
|
|
205
|
+
string: '{{[[table]]}}',
|
|
206
|
+
location: {
|
|
207
|
+
'parent-uid': parentUid,
|
|
208
|
+
order: params.order ?? 'last'
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
// Create columns (headers)
|
|
212
|
+
const columnPlaceholders = [];
|
|
213
|
+
for (let i = 0; i < params.headers.length; i++) {
|
|
214
|
+
const colPlaceholder = registerPlaceholder(`_col_${i}_${Date.now()}`, context);
|
|
215
|
+
columnPlaceholders.push(colPlaceholder);
|
|
216
|
+
actions.push({
|
|
217
|
+
action: 'create-block',
|
|
218
|
+
uid: colPlaceholder,
|
|
219
|
+
string: params.headers[i] || ' ',
|
|
220
|
+
location: {
|
|
221
|
+
'parent-uid': tableContainerPlaceholder,
|
|
222
|
+
order: i
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Create rows under each column
|
|
227
|
+
for (let rowIdx = 0; rowIdx < params.rows.length; rowIdx++) {
|
|
228
|
+
const row = params.rows[rowIdx];
|
|
229
|
+
// First column gets the row label
|
|
230
|
+
actions.push({
|
|
231
|
+
action: 'create-block',
|
|
232
|
+
string: row.label || ' ',
|
|
233
|
+
location: {
|
|
234
|
+
'parent-uid': columnPlaceholders[0],
|
|
235
|
+
order: rowIdx
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
// Remaining columns get the cells
|
|
239
|
+
for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx++) {
|
|
240
|
+
actions.push({
|
|
241
|
+
action: 'create-block',
|
|
242
|
+
string: row.cells[cellIdx] || ' ',
|
|
243
|
+
location: {
|
|
244
|
+
'parent-uid': columnPlaceholders[cellIdx + 1],
|
|
245
|
+
order: rowIdx
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return actions;
|
|
251
|
+
}
|
|
252
|
+
function translateOutline(cmd, context) {
|
|
253
|
+
const { params } = cmd;
|
|
254
|
+
const parentUid = getParentUid(params, context);
|
|
255
|
+
const actions = [];
|
|
256
|
+
// Reset level stack with outline parent
|
|
257
|
+
context.currentParent = parentUid;
|
|
258
|
+
context.levelStack = [];
|
|
259
|
+
for (const item of params.items) {
|
|
260
|
+
let itemParentUid;
|
|
261
|
+
if (item.level === 1) {
|
|
262
|
+
itemParentUid = parentUid;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
const levelParent = getParentFromLevel(item.level, context);
|
|
266
|
+
if (!levelParent) {
|
|
267
|
+
throw new Error(`Invalid outline hierarchy: level ${item.level} has no parent`);
|
|
268
|
+
}
|
|
269
|
+
itemParentUid = levelParent;
|
|
270
|
+
}
|
|
271
|
+
const action = {
|
|
272
|
+
action: 'create-block',
|
|
273
|
+
string: item.text,
|
|
274
|
+
location: {
|
|
275
|
+
'parent-uid': itemParentUid,
|
|
276
|
+
order: 'last'
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
// Register placeholder
|
|
280
|
+
const placeholderName = item.as || `_outline_${actions.length}_${Date.now()}`;
|
|
281
|
+
action.uid = registerPlaceholder(placeholderName, context);
|
|
282
|
+
if (item.heading) {
|
|
283
|
+
action.heading = item.heading;
|
|
284
|
+
}
|
|
285
|
+
// Update level stack
|
|
286
|
+
updateLevelStack(item.level, action.uid, context);
|
|
287
|
+
actions.push(action);
|
|
288
|
+
}
|
|
289
|
+
return actions;
|
|
290
|
+
}
|
|
291
|
+
function translateRemember(cmd, context) {
|
|
292
|
+
const { params } = cmd;
|
|
293
|
+
// Build memory text with categories
|
|
294
|
+
let memoryText = params.text;
|
|
295
|
+
if (params.categories && params.categories.length > 0) {
|
|
296
|
+
const tags = params.categories.map(cat => `#[[${cat}]]`).join(' ');
|
|
297
|
+
memoryText = `${params.text} ${tags}`;
|
|
298
|
+
}
|
|
299
|
+
// Add MEMORIES_TAG if configured (we'll handle this in the CLI command)
|
|
300
|
+
// For now, just create the block
|
|
301
|
+
let parentUid;
|
|
302
|
+
// If heading is specified, we'd need to look it up or create it
|
|
303
|
+
// For simplicity, require parent UID when heading is used
|
|
304
|
+
// TODO: Add heading resolution support
|
|
305
|
+
if (params.heading && !params.parent) {
|
|
306
|
+
throw new Error('remember with --heading requires --parent or heading resolution not yet implemented');
|
|
307
|
+
}
|
|
308
|
+
parentUid = getParentUid(params, context);
|
|
309
|
+
const action = {
|
|
310
|
+
action: 'create-block',
|
|
311
|
+
string: memoryText,
|
|
312
|
+
location: {
|
|
313
|
+
'parent-uid': parentUid,
|
|
314
|
+
order: params.order ?? 'last'
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
if (params.as) {
|
|
318
|
+
action.uid = registerPlaceholder(params.as, context);
|
|
319
|
+
}
|
|
320
|
+
return [action];
|
|
321
|
+
}
|
|
322
|
+
function translatePage(cmd, context) {
|
|
323
|
+
// Note: Page creation uses create-page API, not batch actions
|
|
324
|
+
// We'll handle this specially in the batch command
|
|
325
|
+
// For now, throw an error to indicate this needs special handling
|
|
326
|
+
throw new Error('page command requires special handling outside batch actions');
|
|
327
|
+
}
|
|
328
|
+
function translateCodeblock(cmd, context) {
|
|
329
|
+
const { params } = cmd;
|
|
330
|
+
const parentUid = getParentUid(params, context);
|
|
331
|
+
// Format code with triple backticks
|
|
332
|
+
const codeContent = '```' + params.language + '\n' + params.code + '\n```';
|
|
333
|
+
const action = {
|
|
334
|
+
action: 'create-block',
|
|
335
|
+
string: codeContent,
|
|
336
|
+
location: {
|
|
337
|
+
'parent-uid': parentUid,
|
|
338
|
+
order: params.order ?? 'last'
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
if (params.as) {
|
|
342
|
+
action.uid = registerPlaceholder(params.as, context);
|
|
343
|
+
}
|
|
344
|
+
return [action];
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Translate all commands to batch actions
|
|
348
|
+
*/
|
|
349
|
+
export function translateAllCommands(commands, context) {
|
|
350
|
+
const actions = [];
|
|
351
|
+
const pageCommands = [];
|
|
352
|
+
for (const cmd of commands) {
|
|
353
|
+
if (cmd.command === 'page') {
|
|
354
|
+
// Collect page commands for special handling
|
|
355
|
+
pageCommands.push(cmd);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
const cmdActions = translateCommand(cmd, context);
|
|
359
|
+
actions.push(...cmdActions);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return { actions, pageCommands };
|
|
363
|
+
}
|