@roomi-fields/notebooklm-mcp 2.0.4 → 2.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/README.md +1 -1
- package/dist/content/content-manager.d.ts +10 -0
- package/dist/content/content-manager.d.ts.map +1 -1
- package/dist/content/content-manager.js +333 -2
- package/dist/content/content-manager.js.map +1 -1
- package/dist/http-wrapper.js +42 -0
- package/dist/http-wrapper.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/index.d.ts +21 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +114 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tool-names.d.ts.map +1 -1
- package/dist/tools/tool-names.js +2 -0
- package/dist/tools/tool-names.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Automate Google NotebookLM at scale. 33-endpoint HTTP REST API for n8n / Zapier / Make / curl, plus an MCP server for Claude Code / Cursor / Codex. Citation-backed Q&A, full Studio generation (audio · video · infographic · report · presentation · data table), multi-account rotation with auto-reauth.**
|
|
6
6
|
|
|
7
|
-
> v2.0
|
|
7
|
+
> v2.1.0 — production-grade, batch-tested on overnight runs of 1 000+ questions. Tools are now a namespaced tree (`notebook_ask`, `source_add`, `session_list`…) with MCP `annotations` + `outputSchema` on every tool — **the old flat names still work as aliases, nothing breaks**. See the [changelog](./CHANGELOG.md) for the full mapping. [Compare with `PleasePrompto/notebooklm-mcp` v2.0.0](https://roomi-fields.github.io/notebooklm-mcp/compare) to see when this project is the right pick (REST API, full Studio, auto-reauth) and when the MCP-only upstream is.
|
|
8
8
|
|
|
9
9
|
<!-- Badges -->
|
|
10
10
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Page } from 'patchright';
|
|
12
12
|
import type { SourceUploadInput, SourceUploadResult, SourceDeleteInput, SourceDeleteResult, ContentType, ContentGenerationInput, ContentGenerationResult, NotebookSource, GeneratedContent, NotebookContentOverview, ContentDownloadResult, NoteInput, NoteResult, SaveChatToNoteInput, SaveChatToNoteResult, NoteToSourceInput, NoteToSourceResult } from './types.js';
|
|
13
|
+
import type { NoteListResult, NoteGetInput, NoteGetResult } from '../types.js';
|
|
13
14
|
export declare class ContentManager {
|
|
14
15
|
private page;
|
|
15
16
|
constructor(page: Page);
|
|
@@ -368,5 +369,14 @@ export declare class ContentManager {
|
|
|
368
369
|
* Extract the content of a note for fallback conversion
|
|
369
370
|
*/
|
|
370
371
|
private extractNoteContent;
|
|
372
|
+
/**
|
|
373
|
+
* List all notes in the NotebookLM Studio panel.
|
|
374
|
+
* Handles scrolling inside the Studio panel to ensure all notes are loaded.
|
|
375
|
+
*/
|
|
376
|
+
listNotes(): Promise<NoteListResult>;
|
|
377
|
+
/**
|
|
378
|
+
* Get the title and text content of a specific note by title or ID.
|
|
379
|
+
*/
|
|
380
|
+
getNoteContent(input: NoteGetInput): Promise<NoteGetResult>;
|
|
371
381
|
}
|
|
372
382
|
//# sourceMappingURL=content-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-manager.d.ts","sourceRoot":"","sources":["../../src/content/content-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,YAAY,CAAC;AAmC/D,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,qBAAqB,EACrB,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"content-manager.d.ts","sourceRoot":"","sources":["../../src/content/content-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -4009,6 +4009,8 @@ export class ContentManager {
|
|
|
4009
4009
|
) {
|
|
4010
4010
|
// Selectors for note items in the Studio panel
|
|
4011
4011
|
const noteItemSelectors = [
|
|
4012
|
+
'artifact-library-note',
|
|
4013
|
+
'[class*="artifact-item-button"]',
|
|
4012
4014
|
'.note-item',
|
|
4013
4015
|
'[data-item="note"]',
|
|
4014
4016
|
'.notes-list-item',
|
|
@@ -4046,7 +4048,7 @@ export class ContentManager {
|
|
|
4046
4048
|
}
|
|
4047
4049
|
// Also check specific title elements
|
|
4048
4050
|
try {
|
|
4049
|
-
const titleText = await el.$eval('.note-title, .title, [class*="title"], [class*="name"], h3, h4', (e) => e.textContent?.trim() || '');
|
|
4051
|
+
const titleText = await el.$eval('.artifact-title, [class*="artifact-title"], .note-title, .title, [class*="title"], [class*="name"], h3, h4', (e) => e.textContent?.trim() || '');
|
|
4050
4052
|
if (titleText.toLowerCase().includes(noteTitle.toLowerCase())) {
|
|
4051
4053
|
return el;
|
|
4052
4054
|
}
|
|
@@ -4248,7 +4250,209 @@ export class ContentManager {
|
|
|
4248
4250
|
// Method 1: Click the note to open it and extract content
|
|
4249
4251
|
await noteElement.click();
|
|
4250
4252
|
await randomDelay(500, 1000);
|
|
4251
|
-
|
|
4253
|
+
/* @SDD:T1.1.1:START Implement robust table and note content DOM walker serialization */
|
|
4254
|
+
// Wait for the note viewer to be visible
|
|
4255
|
+
try {
|
|
4256
|
+
await this.page.waitForSelector('labs-tailwind-doc-viewer:visible, note-editor:visible, .ProseMirror:visible', { timeout: 5000 });
|
|
4257
|
+
}
|
|
4258
|
+
catch {
|
|
4259
|
+
log.warning(' ⚠️ Timeout waiting for note viewer to be visible, attempting fallback...');
|
|
4260
|
+
}
|
|
4261
|
+
// Try serialization on the active viewer element
|
|
4262
|
+
const activeViewerSelector = 'labs-tailwind-doc-viewer:visible, note-editor:visible, .ProseMirror:visible';
|
|
4263
|
+
const activeViewer = await this.page.$(activeViewerSelector);
|
|
4264
|
+
if (activeViewer) {
|
|
4265
|
+
const serialized = await activeViewer.evaluate((viewerEl) => {
|
|
4266
|
+
// Inline serialization helper for text nodes and elements
|
|
4267
|
+
function serializeInline(node) {
|
|
4268
|
+
let text = '';
|
|
4269
|
+
const childNodes = Array.from(node.childNodes);
|
|
4270
|
+
if (childNodes.length === 0) {
|
|
4271
|
+
return node.textContent || '';
|
|
4272
|
+
}
|
|
4273
|
+
for (const child of childNodes) {
|
|
4274
|
+
if (child.nodeType === 3) {
|
|
4275
|
+
// Node.TEXT_NODE
|
|
4276
|
+
text += child.textContent;
|
|
4277
|
+
}
|
|
4278
|
+
else if (child.nodeType === 1) {
|
|
4279
|
+
// Node.ELEMENT_NODE
|
|
4280
|
+
const elNode = child;
|
|
4281
|
+
const tagName = elNode.tagName.toLowerCase();
|
|
4282
|
+
const childText = serializeInline(elNode);
|
|
4283
|
+
if (tagName === 'b' || tagName === 'strong') {
|
|
4284
|
+
text += `**${childText}**`;
|
|
4285
|
+
}
|
|
4286
|
+
else if (tagName === 'i' || tagName === 'em') {
|
|
4287
|
+
text += `*${childText}*`;
|
|
4288
|
+
}
|
|
4289
|
+
else if (tagName === 'code' || elNode.classList.contains('code')) {
|
|
4290
|
+
text += `\`${childText}\``;
|
|
4291
|
+
}
|
|
4292
|
+
else if (tagName === 'a') {
|
|
4293
|
+
const href = elNode.getAttribute('href') || '';
|
|
4294
|
+
text += `[${childText}](${href})`;
|
|
4295
|
+
}
|
|
4296
|
+
else {
|
|
4297
|
+
text += childText;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
return text;
|
|
4302
|
+
}
|
|
4303
|
+
const elementListRenderer = viewerEl.querySelector('element-list-renderer');
|
|
4304
|
+
if (!elementListRenderer) {
|
|
4305
|
+
// ProseMirror / semantic HTML fallback
|
|
4306
|
+
const editor = viewerEl.querySelector('.ProseMirror') || viewerEl;
|
|
4307
|
+
function serializeNode(node) {
|
|
4308
|
+
if (node.nodeType === 3) {
|
|
4309
|
+
// Node.TEXT_NODE
|
|
4310
|
+
return node.textContent || '';
|
|
4311
|
+
}
|
|
4312
|
+
if (node.nodeType !== 1) {
|
|
4313
|
+
// Node.ELEMENT_NODE
|
|
4314
|
+
return '';
|
|
4315
|
+
}
|
|
4316
|
+
const elNode = node;
|
|
4317
|
+
const tagName = elNode.tagName.toLowerCase();
|
|
4318
|
+
if (tagName === 'b' || tagName === 'strong') {
|
|
4319
|
+
return `**${Array.from(elNode.childNodes).map(serializeNode).join('')}**`;
|
|
4320
|
+
}
|
|
4321
|
+
if (tagName === 'i' || tagName === 'em') {
|
|
4322
|
+
return `*${Array.from(elNode.childNodes).map(serializeNode).join('')}*`;
|
|
4323
|
+
}
|
|
4324
|
+
if (tagName === 'code') {
|
|
4325
|
+
return `\`${elNode.textContent || ''}\``;
|
|
4326
|
+
}
|
|
4327
|
+
if (tagName === 'p') {
|
|
4328
|
+
return `${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
|
|
4329
|
+
}
|
|
4330
|
+
if (tagName === 'h1') {
|
|
4331
|
+
return `# ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
|
|
4332
|
+
}
|
|
4333
|
+
if (tagName === 'h2') {
|
|
4334
|
+
return `## ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
|
|
4335
|
+
}
|
|
4336
|
+
if (tagName === 'h3') {
|
|
4337
|
+
return `### ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n\n`;
|
|
4338
|
+
}
|
|
4339
|
+
if (tagName === 'ul') {
|
|
4340
|
+
return `${Array.from(elNode.childNodes).map(serializeNode).join('')}\n`;
|
|
4341
|
+
}
|
|
4342
|
+
if (tagName === 'li') {
|
|
4343
|
+
return `* ${Array.from(elNode.childNodes).map(serializeNode).join('')}\n`;
|
|
4344
|
+
}
|
|
4345
|
+
if (tagName === 'table') {
|
|
4346
|
+
let tableMd = '\n';
|
|
4347
|
+
const rows = Array.from(elNode.querySelectorAll('tr'));
|
|
4348
|
+
rows.forEach((row, rowIndex) => {
|
|
4349
|
+
const cells = Array.from(row.querySelectorAll('th, td'));
|
|
4350
|
+
const cellTexts = cells.map((cell) => Array.from(cell.childNodes).map(serializeNode).join('').trim());
|
|
4351
|
+
tableMd += `| ${cellTexts.join(' | ')} |\n`;
|
|
4352
|
+
if (rowIndex === 0) {
|
|
4353
|
+
const separator = cellTexts.map(() => '---');
|
|
4354
|
+
tableMd += `| ${separator.join(' | ')} |\n`;
|
|
4355
|
+
}
|
|
4356
|
+
});
|
|
4357
|
+
return tableMd + '\n';
|
|
4358
|
+
}
|
|
4359
|
+
if (tagName === 'pre') {
|
|
4360
|
+
return `\n\`\`\`\n${elNode.textContent?.trim() || ''}\n\`\`\`\n\n`;
|
|
4361
|
+
}
|
|
4362
|
+
return Array.from(elNode.childNodes).map(serializeNode).join('');
|
|
4363
|
+
}
|
|
4364
|
+
return Array.from(editor.childNodes)
|
|
4365
|
+
.map(serializeNode)
|
|
4366
|
+
.join('')
|
|
4367
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
4368
|
+
.trim();
|
|
4369
|
+
}
|
|
4370
|
+
let markdown = '';
|
|
4371
|
+
const structuralElements = Array.from(elementListRenderer.children);
|
|
4372
|
+
function processStructuralElement(el) {
|
|
4373
|
+
const tableView = el.querySelector('table-element-view');
|
|
4374
|
+
if (tableView) {
|
|
4375
|
+
const table = tableView.querySelector('table');
|
|
4376
|
+
if (table) {
|
|
4377
|
+
const rows = Array.from(table.querySelectorAll('tr'));
|
|
4378
|
+
if (rows.length === 0)
|
|
4379
|
+
return;
|
|
4380
|
+
let tableMarkdown = '\n';
|
|
4381
|
+
rows.forEach((row, rowIndex) => {
|
|
4382
|
+
const cells = Array.from(row.querySelectorAll('th, td'));
|
|
4383
|
+
const cellTexts = cells.map((cell) => {
|
|
4384
|
+
const paragraph = cell.querySelector('.paragraph');
|
|
4385
|
+
if (paragraph) {
|
|
4386
|
+
return serializeInline(paragraph).trim();
|
|
4387
|
+
}
|
|
4388
|
+
return serializeInline(cell).trim();
|
|
4389
|
+
});
|
|
4390
|
+
tableMarkdown += `| ${cellTexts.join(' | ')} |\n`;
|
|
4391
|
+
if (rowIndex === 0) {
|
|
4392
|
+
const separator = cellTexts.map(() => '---');
|
|
4393
|
+
tableMarkdown += `| ${separator.join(' | ')} |\n`;
|
|
4394
|
+
}
|
|
4395
|
+
});
|
|
4396
|
+
markdown += tableMarkdown + '\n';
|
|
4397
|
+
}
|
|
4398
|
+
return;
|
|
4399
|
+
}
|
|
4400
|
+
const paragraphView = el.querySelector('paragraph-element-view');
|
|
4401
|
+
if (paragraphView) {
|
|
4402
|
+
const textContainer = paragraphView.querySelector('.paragraph');
|
|
4403
|
+
if (textContainer) {
|
|
4404
|
+
const text = serializeInline(textContainer).trim();
|
|
4405
|
+
if (!text)
|
|
4406
|
+
return;
|
|
4407
|
+
if (textContainer.classList.contains('list-item') ||
|
|
4408
|
+
textContainer.tagName.toLowerCase() === 'li') {
|
|
4409
|
+
markdown += `* ${text}\n`;
|
|
4410
|
+
}
|
|
4411
|
+
else if (textContainer.classList.contains('heading1')) {
|
|
4412
|
+
markdown += `\n# ${text}\n\n`;
|
|
4413
|
+
}
|
|
4414
|
+
else if (textContainer.classList.contains('heading2')) {
|
|
4415
|
+
markdown += `\n## ${text}\n\n`;
|
|
4416
|
+
}
|
|
4417
|
+
else if (textContainer.classList.contains('heading3')) {
|
|
4418
|
+
markdown += `\n### ${text}\n\n`;
|
|
4419
|
+
}
|
|
4420
|
+
else {
|
|
4421
|
+
if (!textContainer.classList.contains('table-paragraph')) {
|
|
4422
|
+
markdown += `${text}\n\n`;
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4426
|
+
return;
|
|
4427
|
+
}
|
|
4428
|
+
const preEl = el.querySelector('pre, code');
|
|
4429
|
+
if (preEl) {
|
|
4430
|
+
markdown += `\n\`\`\`\n${preEl.textContent?.trim() || ''}\n\`\`\`\n\n`;
|
|
4431
|
+
return;
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
for (const structuralEl of structuralElements) {
|
|
4435
|
+
const tagName = structuralEl.tagName.toLowerCase();
|
|
4436
|
+
if (tagName !== 'labs-tailwind-structural-element-view-v2') {
|
|
4437
|
+
if (tagName === 'ul' || tagName === 'ol') {
|
|
4438
|
+
const nestedElements = Array.from(structuralEl.querySelectorAll('labs-tailwind-structural-element-view-v2'));
|
|
4439
|
+
for (const nestedEl of nestedElements) {
|
|
4440
|
+
processStructuralElement(nestedEl);
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
continue;
|
|
4444
|
+
}
|
|
4445
|
+
processStructuralElement(structuralEl);
|
|
4446
|
+
}
|
|
4447
|
+
return markdown.replace(/\n{3,}/g, '\n\n').trim();
|
|
4448
|
+
});
|
|
4449
|
+
if (serialized && serialized.length > 10) {
|
|
4450
|
+
log.info(` ✅ Serialized note content via DOM walker (${serialized.length} chars)`);
|
|
4451
|
+
return noteTitle ? `# ${noteTitle}\n\n${serialized}` : serialized;
|
|
4452
|
+
}
|
|
4453
|
+
}
|
|
4454
|
+
/* @SDD:T1.1.1:END */
|
|
4455
|
+
// Fallback method to existing selectors
|
|
4252
4456
|
const contentSelectors = [
|
|
4253
4457
|
// Note body/content areas
|
|
4254
4458
|
'.note-content',
|
|
@@ -4318,5 +4522,132 @@ export class ContentManager {
|
|
|
4318
4522
|
return null;
|
|
4319
4523
|
}
|
|
4320
4524
|
}
|
|
4525
|
+
/**
|
|
4526
|
+
* List all notes in the NotebookLM Studio panel.
|
|
4527
|
+
* Handles scrolling inside the Studio panel to ensure all notes are loaded.
|
|
4528
|
+
*/
|
|
4529
|
+
async listNotes() {
|
|
4530
|
+
log.info(`📚 Listing notes in Studio panel...`);
|
|
4531
|
+
try {
|
|
4532
|
+
// Step 1: Ensure we are in the Studio panel
|
|
4533
|
+
await this.navigateToStudio();
|
|
4534
|
+
await randomDelay(1000, 2000);
|
|
4535
|
+
// Step 2: Locate the scrollable container in the Studio panel
|
|
4536
|
+
const scrollContainerSelector = '.studio-panel, [class*="studio-panel"]';
|
|
4537
|
+
const containerExists = (await this.page.locator(scrollContainerSelector).count()) > 0;
|
|
4538
|
+
if (containerExists) {
|
|
4539
|
+
// Scroll the panel down a few times to trigger lazy load of old notes
|
|
4540
|
+
log.info(' ⏳ Scrolling Studio panel to load all notes...');
|
|
4541
|
+
for (let i = 0; i < 5; i++) {
|
|
4542
|
+
await this.page.evaluate((selector) => {
|
|
4543
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4544
|
+
const panel = globalThis.document.querySelector(selector);
|
|
4545
|
+
if (panel) {
|
|
4546
|
+
panel.scrollTop = panel.scrollHeight;
|
|
4547
|
+
}
|
|
4548
|
+
}, scrollContainerSelector);
|
|
4549
|
+
await randomDelay(300, 500);
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
// Step 3: Scrape note elements from the DOM
|
|
4553
|
+
const notes = [];
|
|
4554
|
+
const seenTitles = new Set();
|
|
4555
|
+
// Selectors based on verified DOM structure
|
|
4556
|
+
const noteItemSelector = 'artifact-library-note, [class*="artifact-item-button"]';
|
|
4557
|
+
const noteElements = await this.page.locator(noteItemSelector).all();
|
|
4558
|
+
log.info(` 🔍 Found ${noteElements.length} candidate note elements`);
|
|
4559
|
+
for (const el of noteElements) {
|
|
4560
|
+
try {
|
|
4561
|
+
const titleText = (await el
|
|
4562
|
+
.locator('.artifact-title, [class*="artifact-title"]')
|
|
4563
|
+
.first()
|
|
4564
|
+
.textContent()) || '';
|
|
4565
|
+
const detailsText = (await el
|
|
4566
|
+
.locator('.artifact-details, [class*="artifact-details"]')
|
|
4567
|
+
.first()
|
|
4568
|
+
.textContent()) || '';
|
|
4569
|
+
const noteId = (await el.getAttribute('data-note-id')) ||
|
|
4570
|
+
(await el.getAttribute('data-id')) ||
|
|
4571
|
+
undefined;
|
|
4572
|
+
const title = titleText.trim();
|
|
4573
|
+
if (title && !seenTitles.has(title)) {
|
|
4574
|
+
seenTitles.add(title);
|
|
4575
|
+
notes.push({
|
|
4576
|
+
id: noteId,
|
|
4577
|
+
title,
|
|
4578
|
+
details: detailsText.trim() || undefined,
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
catch {
|
|
4583
|
+
continue;
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
log.success(` ✅ Successfully listed ${notes.length} notes`);
|
|
4587
|
+
return { success: true, notes };
|
|
4588
|
+
}
|
|
4589
|
+
catch (error) {
|
|
4590
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4591
|
+
log.error(` ❌ Failed to list notes: ${errorMsg}`);
|
|
4592
|
+
return { success: false, notes: [], error: errorMsg };
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
/**
|
|
4596
|
+
* Get the title and text content of a specific note by title or ID.
|
|
4597
|
+
*/
|
|
4598
|
+
async getNoteContent(input) {
|
|
4599
|
+
const { noteTitle, noteId } = input;
|
|
4600
|
+
log.info(`📄 Fetching content of note: "${noteTitle || noteId}"`);
|
|
4601
|
+
try {
|
|
4602
|
+
await this.navigateToStudio();
|
|
4603
|
+
await randomDelay(500, 1000);
|
|
4604
|
+
// Reuse existing helper to find the note element
|
|
4605
|
+
const noteElement = await this.findNoteElement(noteTitle, noteId);
|
|
4606
|
+
if (!noteElement) {
|
|
4607
|
+
return {
|
|
4608
|
+
success: false,
|
|
4609
|
+
title: noteTitle || '',
|
|
4610
|
+
content: '',
|
|
4611
|
+
error: `Note not found: "${noteTitle || noteId}"`,
|
|
4612
|
+
};
|
|
4613
|
+
}
|
|
4614
|
+
// Reuse existing helper to click note, wait, and extract content text
|
|
4615
|
+
const content = await this.extractNoteContent(noteElement, noteTitle);
|
|
4616
|
+
// Close the note popover/editor if open to return to clean state
|
|
4617
|
+
try {
|
|
4618
|
+
await this.page.keyboard.press('Escape');
|
|
4619
|
+
await randomDelay(200, 400);
|
|
4620
|
+
}
|
|
4621
|
+
catch {
|
|
4622
|
+
// Ignore escape errors
|
|
4623
|
+
}
|
|
4624
|
+
if (content) {
|
|
4625
|
+
log.success(` ✅ Retrieved note content (${content.length} characters)`);
|
|
4626
|
+
return {
|
|
4627
|
+
success: true,
|
|
4628
|
+
title: noteTitle || '',
|
|
4629
|
+
content,
|
|
4630
|
+
};
|
|
4631
|
+
}
|
|
4632
|
+
else {
|
|
4633
|
+
return {
|
|
4634
|
+
success: false,
|
|
4635
|
+
title: noteTitle || '',
|
|
4636
|
+
content: '',
|
|
4637
|
+
error: 'Could not extract content from note editor.',
|
|
4638
|
+
};
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4641
|
+
catch (error) {
|
|
4642
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4643
|
+
log.error(` ❌ Failed to get note content: ${errorMsg}`);
|
|
4644
|
+
return {
|
|
4645
|
+
success: false,
|
|
4646
|
+
title: noteTitle || '',
|
|
4647
|
+
content: '',
|
|
4648
|
+
error: errorMsg,
|
|
4649
|
+
};
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4321
4652
|
}
|
|
4322
4653
|
//# sourceMappingURL=content-manager.js.map
|