@roomi-fields/notebooklm-mcp 1.5.0 → 1.5.1
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 +22 -22
- package/README.md +2 -0
- package/deployment/INDEX.md +292 -0
- package/deployment/PACKAGE-FILES.txt +180 -0
- package/deployment/QUICK-START.md +100 -0
- package/deployment/docs/01-INSTALL.md +611 -0
- package/deployment/docs/02-CONFIGURATION.md +404 -0
- package/deployment/docs/03-API.md +1691 -0
- package/deployment/docs/04-N8N-INTEGRATION.md +373 -0
- package/deployment/docs/05-TROUBLESHOOTING.md +429 -0
- package/deployment/docs/06-NOTEBOOK-LIBRARY.md +692 -0
- package/deployment/docs/07-AUTO-DISCOVERY.md +236 -0
- package/deployment/docs/08-WSL-USAGE.md +363 -0
- package/deployment/docs/09-MULTI-INTERFACE.md +293 -0
- package/deployment/docs/10-CONTENT-MANAGEMENT.md +421 -0
- package/deployment/docs/11-MULTI-ACCOUNT.md +295 -0
- package/deployment/docs/README.md +207 -0
- package/deployment/scripts/README.md +564 -0
- package/deployment/scripts/install.ps1 +114 -0
- package/deployment/scripts/setup-auth.ps1 +217 -0
- package/deployment/scripts/start-server.ps1 +72 -0
- package/deployment/scripts/stop-server.ps1 +51 -0
- package/deployment/scripts/test-api.ps1 +651 -0
- package/deployment/scripts/test-auth.ps1 +323 -0
- package/deployment/scripts/test-auto-discovery.ps1 +295 -0
- package/deployment/scripts/test-cors.ps1 +398 -0
- package/deployment/scripts/test-errors.ps1 +581 -0
- package/deployment/scripts/test-server.ps1 +140 -0
- package/deployment/scripts/test-sessions.ps1 +426 -0
- package/deployment/scripts/test-validation.ps1 +299 -0
- package/dist/cli/accounts.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -1
- package/dist/content/content-manager.d.ts.map +1 -1
- package/dist/content/content-manager.js +113 -118
- package/dist/content/content-manager.js.map +1 -1
- package/dist/i18n/en.json +120 -0
- package/dist/i18n/fr.json +120 -0
- package/dist/i18n/index.d.ts +168 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +213 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/session/browser-session.d.ts.map +1 -1
- package/dist/session/browser-session.js +1 -0
- package/dist/session/browser-session.js.map +1 -1
- package/dist/session/shared-context-manager.d.ts.map +1 -1
- package/dist/session/shared-context-manager.js +2 -0
- package/dist/session/shared-context-manager.js.map +1 -1
- package/docs/ADDING_A_LANGUAGE.md +209 -0
- package/package.json +6 -3
- package/scripts/archive/add-and-activate-notebook.ps1 +31 -0
- package/scripts/archive/add-new-notebook.ps1 +25 -0
- package/scripts/archive/add-rom1pey.ps1 +2 -0
- package/scripts/archive/add-rpmonster.ps1 +2 -0
- package/scripts/archive/add-source-debug.ps1 +11 -0
- package/scripts/archive/add-source-e2e.ps1 +28 -0
- package/scripts/archive/add-source-visible.ps1 +11 -0
- package/scripts/archive/add-test-notebook.ps1 +13 -0
- package/scripts/archive/add-test-source.ps1 +50 -0
- package/scripts/archive/capture-screen.ps1 +11 -0
- package/scripts/archive/change-language.mjs +45 -0
- package/scripts/archive/change-language.ts +44 -0
- package/scripts/archive/check-account.ps1 +19 -0
- package/scripts/archive/check-notebook-2.ps1 +8 -0
- package/scripts/archive/check-test-notebook.ps1 +11 -0
- package/scripts/archive/create-notebook-auto.ps1 +31 -0
- package/scripts/archive/create-notebook.ps1 +8 -0
- package/scripts/archive/create-rom1pey-notebook.ps1 +19 -0
- package/scripts/archive/create-rom1pey.ps1 +8 -0
- package/scripts/archive/create-test-notebook-fresh.ps1 +21 -0
- package/scripts/archive/create-test-notebook.ps1 +16 -0
- package/scripts/archive/debug-add-source-auto.ps1 +29 -0
- package/scripts/archive/debug-add-source.ps1 +19 -0
- package/scripts/archive/debug-add-text-source.ps1 +47 -0
- package/scripts/archive/debug-home.ps1 +10 -0
- package/scripts/archive/debug-selectors.ps1 +55 -0
- package/scripts/archive/debug-sources-panel.ps1 +22 -0
- package/scripts/archive/debug-ui.ps1 +17 -0
- package/scripts/archive/discover-home.ps1 +26 -0
- package/scripts/archive/kill-automation-chrome.ps1 +37 -0
- package/scripts/archive/list-my-notebooks.ps1 +27 -0
- package/scripts/archive/navigate-home-visible.ps1 +23 -0
- package/scripts/archive/navigate-home.ps1 +15 -0
- package/scripts/archive/run-e2e-english.ps1 +111 -0
- package/scripts/archive/run-e2e-rom1pey-v2.ps1 +122 -0
- package/scripts/archive/run-e2e-rom1pey.ps1 +117 -0
- package/scripts/archive/setup-english-test.ps1 +36 -0
- package/scripts/archive/setup-test-notebook.ps1 +71 -0
- package/scripts/archive/simple-add-source.ps1 +14 -0
- package/scripts/archive/t10.ps1 +2 -0
- package/scripts/archive/t20.ps1 +4 -0
- package/scripts/archive/t30.ps1 +9 -0
- package/scripts/archive/t31.ps1 +11 -0
- package/scripts/archive/t32.ps1 +6 -0
- package/scripts/archive/t39.ps1 +5 -0
- package/scripts/archive/t40.ps1 +5 -0
- package/scripts/archive/t53.ps1 +12 -0
- package/scripts/archive/t54.ps1 +12 -0
- package/scripts/archive/t55.ps1 +11 -0
- package/scripts/archive/t9.ps1 +1 -0
- package/scripts/archive/test-access.ps1 +28 -0
- package/scripts/archive/test-add-delete-source.ps1 +64 -0
- package/scripts/archive/test-add-source-visible.ps1 +16 -0
- package/scripts/archive/test-add-source.ps1 +19 -0
- package/scripts/archive/test-add-text-debug.ps1 +28 -0
- package/scripts/archive/test-add-text-source.ps1 +8 -0
- package/scripts/archive/test-add-url-source.ps1 +7 -0
- package/scripts/archive/test-ask-ascii.ps1 +20 -0
- package/scripts/archive/test-ask-cnv.ps1 +20 -0
- package/scripts/archive/test-ask-headed.ps1 +51 -0
- package/scripts/archive/test-ask-ifs.ps1 +16 -0
- package/scripts/archive/test-ask-now.ps1 +24 -0
- package/scripts/archive/test-ask-real.ps1 +19 -0
- package/scripts/archive/test-ask-visible.ps1 +20 -0
- package/scripts/archive/test-create-notebook.ps1 +8 -0
- package/scripts/archive/test-create-then-add.ps1 +17 -0
- package/scripts/archive/test-delete-source.ps1 +41 -0
- package/scripts/archive/test-e2e-notebook.ps1 +21 -0
- package/scripts/archive/test-english-notebook.ps1 +20 -0
- package/scripts/archive/test-english.ps1 +7 -0
- package/scripts/archive/test-full-custom-instructions.ps1 +40 -0
- package/scripts/archive/test-full-infographic.ps1 +34 -0
- package/scripts/archive/test-full-language.ps1 +21 -0
- package/scripts/archive/test-full-presentation.ps1 +85 -0
- package/scripts/archive/test-full-report.ps1 +34 -0
- package/scripts/archive/test-full-source-selection.ps1 +35 -0
- package/scripts/archive/test-full-video-brief.ps1 +22 -0
- package/scripts/archive/test-full-video-explainer.ps1 +22 -0
- package/scripts/archive/test-full-video-styles.ps1 +37 -0
- package/scripts/archive/test-generate-report.ps1 +15 -0
- package/scripts/archive/test-generate-study-guide.ps1 +11 -0
- package/scripts/archive/test-headed-ask.ps1 +13 -0
- package/scripts/archive/test-headed-now.ps1 +9 -0
- package/scripts/archive/test-headed.ps1 +9 -0
- package/scripts/archive/test-hello.ps1 +7 -0
- package/scripts/archive/test-i18n-studio.ps1 +8 -0
- package/scripts/archive/test-i18n.ps1 +7 -0
- package/scripts/archive/test-manual-headed.ps1 +26 -0
- package/scripts/archive/test-mathieu-quota.ps1 +8 -0
- package/scripts/archive/test-notebook-1.ps1 +10 -0
- package/scripts/archive/test-notebook-2-sources.ps1 +12 -0
- package/scripts/archive/test-notebook1.ps1 +14 -0
- package/scripts/archive/test-personal-notebook.ps1 +14 -0
- package/scripts/archive/test-rate-limit.ps1 +19 -0
- package/scripts/archive/test-real-ask.ps1 +50 -0
- package/scripts/archive/test-real-ask2.ps1 +30 -0
- package/scripts/archive/test-rom1pey.ps1 +7 -0
- package/scripts/archive/test-rotation-complete.ps1 +14 -0
- package/scripts/archive/test-rotation.ps1 +8 -0
- package/scripts/archive/test-show-browser.ps1 +39 -0
- package/scripts/archive/test-update-notebook.ps1 +4 -0
- package/scripts/archive/verify-language-slow.ps1 +21 -0
- package/scripts/archive/verify-language.ps1 +15 -0
- package/scripts/check-server.ps1 +46 -0
- package/scripts/mcp-wsl-helper.sh +146 -0
- package/scripts/start-server.ps1 +94 -0
- package/scripts/stop-server.ps1 +30 -0
- package/scripts/switch-account-language.sh +191 -0
- package/scripts/test-account.ps1 +58 -0
|
@@ -14,6 +14,20 @@ import { randomDelay, realisticClick, humanType } from '../utils/stealth-utils.j
|
|
|
14
14
|
import { log } from '../utils/logger.js';
|
|
15
15
|
import { CONFIG } from '../config.js';
|
|
16
16
|
import { waitForLatestAnswer, snapshotAllResponses, isErrorMessage } from '../utils/page-utils.js';
|
|
17
|
+
import { setLocale, tAll } from '../i18n/index.js';
|
|
18
|
+
// Initialize i18n with configured locale
|
|
19
|
+
setLocale(CONFIG.uiLocale);
|
|
20
|
+
/**
|
|
21
|
+
* Build selectors for all supported locales
|
|
22
|
+
* @param template Selector template with {text} placeholder
|
|
23
|
+
* @param category i18n category (e.g., 'tabs', 'buttons')
|
|
24
|
+
* @param key i18n key within the category
|
|
25
|
+
* @returns Array of selectors for all locales
|
|
26
|
+
*/
|
|
27
|
+
function i18nSelectors(template, category, key) {
|
|
28
|
+
const texts = tAll(category, key);
|
|
29
|
+
return texts.map((text) => template.replace('{text}', text));
|
|
30
|
+
}
|
|
17
31
|
import { ContentGenerator } from './content-generator.js';
|
|
18
32
|
// Note: UI selectors are defined inline in methods for better maintainability
|
|
19
33
|
// as NotebookLM's UI may change frequently
|
|
@@ -92,9 +106,8 @@ export class ContentManager {
|
|
|
92
106
|
// Icon button with "add" icon specifically
|
|
93
107
|
'button:has(mat-icon:has-text("add"))',
|
|
94
108
|
'button:has(mat-icon:has-text("add_circle"))',
|
|
95
|
-
// Text-based patterns
|
|
96
|
-
'button:has-text("
|
|
97
|
-
'button:has-text("Ajouter une source")',
|
|
109
|
+
// Text-based patterns (bilingual via i18n)
|
|
110
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'addSource'),
|
|
98
111
|
// FAB buttons (floating action button for adding)
|
|
99
112
|
'button.mat-fab',
|
|
100
113
|
'button.mat-mini-fab',
|
|
@@ -156,10 +169,10 @@ export class ContentManager {
|
|
|
156
169
|
/* no dialog */
|
|
157
170
|
}
|
|
158
171
|
const sourcesTabSelectors = [
|
|
159
|
-
// NotebookLM current UI (Dec 2024) - MDC tabs
|
|
160
|
-
'div.mdc-tab:has-text("
|
|
161
|
-
'.mat-mdc-tab:has-text("
|
|
162
|
-
'[role="tab"]:has-text("
|
|
172
|
+
// NotebookLM current UI (Dec 2024) - MDC tabs (bilingual FR/EN via i18n)
|
|
173
|
+
...i18nSelectors('div.mdc-tab:has-text("{text}")', 'tabs', 'sources'),
|
|
174
|
+
...i18nSelectors('.mat-mdc-tab:has-text("{text}")', 'tabs', 'sources'),
|
|
175
|
+
...i18nSelectors('[role="tab"]:has-text("{text}")', 'tabs', 'sources'),
|
|
163
176
|
// First tab in the tab list (Sources is typically first)
|
|
164
177
|
'.mat-mdc-tab-list .mdc-tab:first-child',
|
|
165
178
|
];
|
|
@@ -281,11 +294,10 @@ export class ContentManager {
|
|
|
281
294
|
}
|
|
282
295
|
log.info(` 📁 Uploading file: ${path.basename(resolvedPath)}`);
|
|
283
296
|
try {
|
|
284
|
-
// Click on file upload option
|
|
297
|
+
// Click on file upload option (bilingual via i18n)
|
|
285
298
|
const fileTypeSelectors = [
|
|
286
|
-
'button:has-text("
|
|
287
|
-
'button:has-text("
|
|
288
|
-
'button:has-text("Upload")',
|
|
299
|
+
...i18nSelectors('button:has-text("{text}")', 'sourceTypes', 'uploadFiles'),
|
|
300
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'upload'),
|
|
289
301
|
'[data-type="file"]',
|
|
290
302
|
];
|
|
291
303
|
for (const selector of fileTypeSelectors) {
|
|
@@ -333,13 +345,11 @@ export class ContentManager {
|
|
|
333
345
|
}
|
|
334
346
|
log.info(` 🌐 Adding URL: ${input.url}`);
|
|
335
347
|
try {
|
|
336
|
-
// Click on URL/Website option
|
|
348
|
+
// Click on URL/Website option (bilingual selectors)
|
|
337
349
|
const urlTypeSelectors = [
|
|
338
|
-
'button:has-text("
|
|
339
|
-
'button:has-text("
|
|
340
|
-
'button:has-text("
|
|
341
|
-
'button:has-text("URL")',
|
|
342
|
-
'button:has-text("Web")',
|
|
350
|
+
...i18nSelectors('button:has-text("{text}")', 'sourceTypes', 'website'),
|
|
351
|
+
...i18nSelectors('button:has-text("{text}")', 'sourceTypes', 'link'),
|
|
352
|
+
...i18nSelectors('button:has-text("{text}")', 'sourceTypes', 'url'),
|
|
343
353
|
'[data-type="url"]',
|
|
344
354
|
'[aria-label*="website"]',
|
|
345
355
|
'[aria-label*="URL"]',
|
|
@@ -363,36 +373,46 @@ export class ContentManager {
|
|
|
363
373
|
}
|
|
364
374
|
if (!foundUrlOption) {
|
|
365
375
|
log.info(` ℹ️ No URL option button found, looking for input directly`);
|
|
376
|
+
// DEBUG: List all visible buttons in the page
|
|
377
|
+
try {
|
|
378
|
+
const buttons = await this.page.locator('button').all();
|
|
379
|
+
log.info(` 🔍 DEBUG: Found ${buttons.length} buttons total`);
|
|
380
|
+
for (let i = 0; i < Math.min(buttons.length, 15); i++) {
|
|
381
|
+
const btn = buttons[i];
|
|
382
|
+
const visible = await btn.isVisible().catch(() => false);
|
|
383
|
+
if (visible) {
|
|
384
|
+
const text = await btn.textContent().catch(() => '');
|
|
385
|
+
const ariaLabel = await btn.getAttribute('aria-label').catch(() => '');
|
|
386
|
+
log.info(` 🔍 Button[${i}]: text="${text?.trim()}", aria="${ariaLabel}"`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (e) {
|
|
391
|
+
log.warning(` ⚠️ Could not list buttons: ${e}`);
|
|
392
|
+
}
|
|
366
393
|
}
|
|
367
394
|
// Wait for input to appear after clicking option
|
|
368
395
|
await randomDelay(500, 1000);
|
|
369
|
-
// Find URL input (can be input OR textarea)
|
|
396
|
+
// Find URL input (can be input OR textarea) - bilingual selectors
|
|
370
397
|
log.info(` 🔍 Looking for URL input...`);
|
|
371
398
|
const urlInputSelectors = [
|
|
372
|
-
//
|
|
373
|
-
'input[placeholder*="
|
|
374
|
-
'textarea[placeholder*="
|
|
375
|
-
'input[placeholder*="
|
|
376
|
-
'textarea[placeholder*="
|
|
377
|
-
|
|
399
|
+
// i18n placeholder selectors
|
|
400
|
+
...i18nSelectors('input[placeholder*="{text}"]', 'placeholders', 'pasteUrl'),
|
|
401
|
+
...i18nSelectors('textarea[placeholder*="{text}"]', 'placeholders', 'pasteUrl'),
|
|
402
|
+
...i18nSelectors('input[placeholder*="{text}"]', 'placeholders', 'enterUrl'),
|
|
403
|
+
...i18nSelectors('textarea[placeholder*="{text}"]', 'placeholders', 'enterUrl'),
|
|
404
|
+
...i18nSelectors('input[placeholder*="{text}"]', 'placeholders', 'pasteLinks'),
|
|
405
|
+
...i18nSelectors('textarea[placeholder*="{text}"]', 'placeholders', 'pasteLinks'),
|
|
406
|
+
// URL/http generic selectors (work in both languages)
|
|
378
407
|
'input[placeholder*="URL"]',
|
|
379
408
|
'textarea[placeholder*="URL"]',
|
|
380
409
|
'input[placeholder*="url"]',
|
|
381
410
|
'textarea[placeholder*="url"]',
|
|
382
411
|
'input[placeholder*="http"]',
|
|
383
412
|
'textarea[placeholder*="http"]',
|
|
384
|
-
'input[placeholder*="Paste"]',
|
|
385
|
-
'textarea[placeholder*="Paste"]',
|
|
386
|
-
'input[placeholder*="Enter"]',
|
|
387
|
-
'textarea[placeholder*="Enter"]',
|
|
388
|
-
'input[placeholder*="Coller"]',
|
|
389
|
-
'textarea[placeholder*="Coller"]',
|
|
390
|
-
'input[placeholder*="link"]',
|
|
391
|
-
'textarea[placeholder*="link"]',
|
|
392
|
-
'input[placeholder*="Link"]',
|
|
393
|
-
'textarea[placeholder*="Link"]',
|
|
394
413
|
'input[name="url"]',
|
|
395
414
|
'input[type="url"]',
|
|
415
|
+
// Fallback dialog selectors
|
|
396
416
|
'[role="dialog"] input[type="text"]',
|
|
397
417
|
'[role="dialog"] input:not([type="hidden"])',
|
|
398
418
|
'[role="dialog"] textarea',
|
|
@@ -491,18 +511,13 @@ export class ContentManager {
|
|
|
491
511
|
}
|
|
492
512
|
log.info(` 📝 Adding text content (${input.text.length} chars)`);
|
|
493
513
|
try {
|
|
494
|
-
// Click on paste text option
|
|
495
|
-
// NotebookLM dialog shows: "Coller du texte" (French) with "Texte copié" span inside a clickable element
|
|
514
|
+
// Click on paste text option (bilingual FR/EN via i18n)
|
|
496
515
|
const textTypeSelectors = [
|
|
497
|
-
//
|
|
498
|
-
'span:has-text("
|
|
499
|
-
':has-text("
|
|
516
|
+
// Span element with pasted text label
|
|
517
|
+
...i18nSelectors('span:has-text("{text}")', 'sourceTypes', 'pastedText'),
|
|
518
|
+
...i18nSelectors(':has-text("{text}")', 'sourceTypes', 'pastedText'),
|
|
500
519
|
// Parent of the span (clickable area)
|
|
501
|
-
'*:has(> span:has-text("
|
|
502
|
-
// English UI
|
|
503
|
-
'span:has-text("Copied text")',
|
|
504
|
-
':has-text("Copied text")',
|
|
505
|
-
'*:has(> span:has-text("Copied text"))',
|
|
520
|
+
...i18nSelectors('*:has(> span:has-text("{text}"))', 'sourceTypes', 'pastedText'),
|
|
506
521
|
// Generic fallbacks
|
|
507
522
|
'span:has-text("Paste text")',
|
|
508
523
|
':has-text("Paste text")',
|
|
@@ -649,9 +664,9 @@ export class ContentManager {
|
|
|
649
664
|
catch (e) {
|
|
650
665
|
log.warning(` ⚠️ Could not take debug screenshot: ${e}`);
|
|
651
666
|
}
|
|
652
|
-
// DEBUG: Check if the "
|
|
667
|
+
// DEBUG: Check if the "Insert" button is enabled (bilingual via i18n)
|
|
653
668
|
try {
|
|
654
|
-
const insertBtnSelectors =
|
|
669
|
+
const insertBtnSelectors = i18nSelectors('button:has-text("{text}")', 'buttons', 'insert');
|
|
655
670
|
for (const sel of insertBtnSelectors) {
|
|
656
671
|
const btn = this.page.locator(sel).first();
|
|
657
672
|
if (await btn.isVisible({ timeout: 500 })) {
|
|
@@ -814,16 +829,14 @@ export class ContentManager {
|
|
|
814
829
|
*/
|
|
815
830
|
async clickUploadButton() {
|
|
816
831
|
const uploadBtnSelectors = [
|
|
817
|
-
// Primary action buttons (most likely)
|
|
818
|
-
'button.mdc-button--raised:has-text("
|
|
819
|
-
'button.mat-flat-button:has-text("
|
|
820
|
-
'button[color="primary"]:has-text("
|
|
821
|
-
// Generic text patterns
|
|
822
|
-
'button:has-text("
|
|
823
|
-
'button:has-text("
|
|
824
|
-
'button:has-text("
|
|
825
|
-
'button:has-text("Ajouter")',
|
|
826
|
-
'button:has-text("Upload")',
|
|
832
|
+
// Primary action buttons (most likely) - bilingual via i18n
|
|
833
|
+
...i18nSelectors('button.mdc-button--raised:has-text("{text}")', 'buttons', 'insert'),
|
|
834
|
+
...i18nSelectors('button.mat-flat-button:has-text("{text}")', 'buttons', 'insert'),
|
|
835
|
+
...i18nSelectors('button[color="primary"]:has-text("{text}")', 'buttons', 'insert'),
|
|
836
|
+
// Generic text patterns (bilingual via i18n)
|
|
837
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'insert'),
|
|
838
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'add'),
|
|
839
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'upload'),
|
|
827
840
|
'button:has-text("Import")',
|
|
828
841
|
'button:has-text("Save")',
|
|
829
842
|
'button:has-text("Submit")',
|
|
@@ -970,24 +983,22 @@ export class ContentManager {
|
|
|
970
983
|
await randomDelay(1000, 2000);
|
|
971
984
|
// METHOD 1: Look for pasted text source in the SOURCES PANEL specifically (not anywhere on page)
|
|
972
985
|
// Use more specific selectors to avoid matching dialog content
|
|
973
|
-
// Support both French ("Texte collé") and English ("Pasted text") UI
|
|
986
|
+
// Support both French ("Texte collé") and English ("Pasted text") UI via i18n
|
|
974
987
|
const pastedTextSelectors = [
|
|
975
|
-
// Sources panel specific selectors
|
|
976
|
-
'mat-checkbox:has-text("
|
|
977
|
-
'[class*="source"]:has-text("
|
|
978
|
-
':has-text("
|
|
979
|
-
// Sources panel specific selectors - English
|
|
980
|
-
'mat-checkbox:has-text("Pasted text")',
|
|
981
|
-
'[class*="source"]:has-text("Pasted text")',
|
|
982
|
-
':has-text("Pasted text"):not([role="dialog"])',
|
|
988
|
+
// Sources panel specific selectors (bilingual via i18n)
|
|
989
|
+
...i18nSelectors('mat-checkbox:has-text("{text}")', 'sourceNames', 'pastedText'),
|
|
990
|
+
...i18nSelectors('[class*="source"]:has-text("{text}")', 'sourceNames', 'pastedText'),
|
|
991
|
+
...i18nSelectors(':has-text("{text}"):not([role="dialog"])', 'sourceNames', 'pastedText'),
|
|
983
992
|
];
|
|
993
|
+
// Get localized pasted text names for detection
|
|
994
|
+
const pastedTextNames = tAll('sourceNames', 'pastedText');
|
|
984
995
|
for (const selector of pastedTextSelectors) {
|
|
985
996
|
try {
|
|
986
997
|
const el = this.page.locator(selector).first();
|
|
987
998
|
if (await el.isVisible({ timeout: 1000 })) {
|
|
988
999
|
log.success(` ✅ Found pasted text source: ${selector}`);
|
|
989
|
-
// Detect source name from selector
|
|
990
|
-
const detectedName = selector.includes(
|
|
1000
|
+
// Detect source name from selector - find which locale's text is in the selector
|
|
1001
|
+
const detectedName = pastedTextNames.find((name) => selector.includes(name)) || pastedTextNames[0];
|
|
991
1002
|
return { success: true, sourceName: detectedName, status: 'ready' };
|
|
992
1003
|
}
|
|
993
1004
|
}
|
|
@@ -1033,7 +1044,8 @@ export class ContentManager {
|
|
|
1033
1044
|
const el = this.page.locator(selector).first();
|
|
1034
1045
|
if (await el.isVisible({ timeout: 1000 })) {
|
|
1035
1046
|
log.success(` ✅ Found pasted text source after wait: ${selector}`);
|
|
1036
|
-
|
|
1047
|
+
// Detect source name from selector - find which locale's text is in the selector
|
|
1048
|
+
const detectedName = pastedTextNames.find((name) => selector.includes(name)) || pastedTextNames[0];
|
|
1037
1049
|
return { success: true, sourceName: detectedName, status: 'ready' };
|
|
1038
1050
|
}
|
|
1039
1051
|
}
|
|
@@ -1650,10 +1662,11 @@ export class ContentManager {
|
|
|
1650
1662
|
// The tabs are: Sources | Discussion | Studio
|
|
1651
1663
|
// Tab class: mdc-tab mat-mdc-tab mat-focus-indicator
|
|
1652
1664
|
const studioSelectors = [
|
|
1653
|
-
|
|
1654
|
-
'.
|
|
1655
|
-
'
|
|
1656
|
-
'
|
|
1665
|
+
// Material Design tabs (bilingual FR/EN via i18n)
|
|
1666
|
+
...i18nSelectors('div.mdc-tab:has-text("{text}")', 'tabs', 'studio'),
|
|
1667
|
+
...i18nSelectors('.mat-mdc-tab:has-text("{text}")', 'tabs', 'studio'),
|
|
1668
|
+
...i18nSelectors('[role="tab"]:has-text("{text}")', 'tabs', 'studio'),
|
|
1669
|
+
...i18nSelectors('div.mdc-tab >> text={text}', 'tabs', 'studio'),
|
|
1657
1670
|
'.notebook-guide', // Legacy fallback
|
|
1658
1671
|
];
|
|
1659
1672
|
for (const selector of studioSelectors) {
|
|
@@ -2131,19 +2144,13 @@ export class ContentManager {
|
|
|
2131
2144
|
*/
|
|
2132
2145
|
async clickDeleteOption() {
|
|
2133
2146
|
const deleteSelectors = [
|
|
2134
|
-
// Menu item selectors
|
|
2135
|
-
'button:has-text("
|
|
2136
|
-
'button:has-text("
|
|
2137
|
-
'
|
|
2138
|
-
'
|
|
2139
|
-
'
|
|
2140
|
-
'
|
|
2141
|
-
'[role="menuitem"]:has-text("Remove")',
|
|
2142
|
-
'[role="menuitem"]:has-text("Retirer")',
|
|
2143
|
-
'mat-menu-item:has-text("Delete")',
|
|
2144
|
-
'mat-menu-item:has-text("Remove")',
|
|
2145
|
-
'.mat-menu-item:has-text("Delete")',
|
|
2146
|
-
'.mat-menu-item:has-text("Remove")',
|
|
2147
|
+
// Menu item selectors (bilingual FR/EN via i18n)
|
|
2148
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'delete'),
|
|
2149
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'remove'),
|
|
2150
|
+
...i18nSelectors('[role="menuitem"]:has-text("{text}")', 'buttons', 'delete'),
|
|
2151
|
+
...i18nSelectors('[role="menuitem"]:has-text("{text}")', 'buttons', 'remove'),
|
|
2152
|
+
...i18nSelectors('mat-menu-item:has-text("{text}")', 'buttons', 'delete'),
|
|
2153
|
+
...i18nSelectors('.mat-menu-item:has-text("{text}")', 'buttons', 'delete'),
|
|
2147
2154
|
// With icons
|
|
2148
2155
|
'button:has(mat-icon:has-text("delete"))',
|
|
2149
2156
|
'[role="menuitem"]:has(mat-icon:has-text("delete"))',
|
|
@@ -2191,13 +2198,10 @@ export class ContentManager {
|
|
|
2191
2198
|
*/
|
|
2192
2199
|
async confirmDeletion() {
|
|
2193
2200
|
const confirmSelectors = [
|
|
2194
|
-
// Confirmation buttons
|
|
2195
|
-
'button:has-text("
|
|
2196
|
-
'button:has-text("
|
|
2197
|
-
'button:has-text("
|
|
2198
|
-
'button:has-text("Oui")',
|
|
2199
|
-
'button:has-text("Delete")',
|
|
2200
|
-
'button:has-text("Supprimer")',
|
|
2201
|
+
// Confirmation buttons (bilingual via i18n)
|
|
2202
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'confirm'),
|
|
2203
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'yes'),
|
|
2204
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'delete'),
|
|
2201
2205
|
'button:has-text("OK")',
|
|
2202
2206
|
// Dialog confirm buttons
|
|
2203
2207
|
'[role="dialog"] button.mat-primary',
|
|
@@ -2467,8 +2471,8 @@ export class ContentManager {
|
|
|
2467
2471
|
'button[aria-label*="Download"]',
|
|
2468
2472
|
'button[aria-label*="Télécharger"]',
|
|
2469
2473
|
'button[aria-label*="download"]',
|
|
2470
|
-
|
|
2471
|
-
'button:has-text("
|
|
2474
|
+
// Text-based patterns (bilingual via i18n)
|
|
2475
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'download'),
|
|
2472
2476
|
'a[download]',
|
|
2473
2477
|
'.download-button',
|
|
2474
2478
|
'[data-action="download"]',
|
|
@@ -2582,13 +2586,11 @@ export class ContentManager {
|
|
|
2582
2586
|
continue;
|
|
2583
2587
|
}
|
|
2584
2588
|
}
|
|
2585
|
-
// Find download button (either direct or in menu)
|
|
2589
|
+
// Find download button (either direct or in menu) - bilingual via i18n
|
|
2586
2590
|
const downloadSelectors = [
|
|
2587
2591
|
// Menu item patterns (if menu was opened)
|
|
2588
|
-
'[role="menuitem"]:has-text("
|
|
2589
|
-
'
|
|
2590
|
-
'mat-menu-item:has-text("Download")',
|
|
2591
|
-
'mat-menu-item:has-text("Télécharger")',
|
|
2592
|
+
...i18nSelectors('[role="menuitem"]:has-text("{text}")', 'buttons', 'download'),
|
|
2593
|
+
...i18nSelectors('mat-menu-item:has-text("{text}")', 'buttons', 'download'),
|
|
2592
2594
|
'.mat-mdc-menu-item:has-text("Download")',
|
|
2593
2595
|
// Material Design icon buttons
|
|
2594
2596
|
'button:has(mat-icon:has-text("download"))',
|
|
@@ -2598,9 +2600,8 @@ export class ContentManager {
|
|
|
2598
2600
|
'button[aria-label*="Download"]',
|
|
2599
2601
|
'button[aria-label*="Télécharger"]',
|
|
2600
2602
|
'button[aria-label*="download"]',
|
|
2601
|
-
// Text patterns
|
|
2602
|
-
'button:has-text("
|
|
2603
|
-
'button:has-text("Télécharger")',
|
|
2603
|
+
// Text patterns (bilingual via i18n)
|
|
2604
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'download'),
|
|
2604
2605
|
// Icon buttons near audio
|
|
2605
2606
|
'.audio-controls button:has(mat-icon)',
|
|
2606
2607
|
'.audio-player button:has(mat-icon)',
|
|
@@ -2860,13 +2861,11 @@ export class ContentManager {
|
|
|
2860
2861
|
// Step 1: Navigate to Studio panel where notes are managed
|
|
2861
2862
|
await this.navigateToStudio();
|
|
2862
2863
|
await randomDelay(500, 1000);
|
|
2863
|
-
// Step 2: Look for "Add note" or "+" button in the Studio panel
|
|
2864
|
+
// Step 2: Look for "Add note" or "+" button in the Studio panel (bilingual via i18n)
|
|
2864
2865
|
const addNoteSelectors = [
|
|
2865
|
-
// Primary selectors for Add Note button
|
|
2866
|
-
'button:has-text("
|
|
2867
|
-
'button:has-text("
|
|
2868
|
-
'button:has-text("New note")',
|
|
2869
|
-
'button:has-text("Nouvelle note")',
|
|
2866
|
+
// Primary selectors for Add Note button (bilingual via i18n)
|
|
2867
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'addNote'),
|
|
2868
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'newNote'),
|
|
2870
2869
|
// Icon button patterns
|
|
2871
2870
|
'button[aria-label*="Add note"]',
|
|
2872
2871
|
'button[aria-label*="add note" i]',
|
|
@@ -3019,17 +3018,13 @@ export class ContentManager {
|
|
|
3019
3018
|
status: 'failed',
|
|
3020
3019
|
};
|
|
3021
3020
|
}
|
|
3022
|
-
// Step 6: Save the note by clicking Save/Done button
|
|
3021
|
+
// Step 6: Save the note by clicking Save/Done button (bilingual via i18n)
|
|
3023
3022
|
const saveSelectors = [
|
|
3024
|
-
// Primary save buttons
|
|
3025
|
-
'button:has-text("
|
|
3026
|
-
'button:has-text("
|
|
3027
|
-
'button:has-text("
|
|
3028
|
-
'button:has-text("
|
|
3029
|
-
'button:has-text("Create")',
|
|
3030
|
-
'button:has-text("Créer")',
|
|
3031
|
-
'button:has-text("Add")',
|
|
3032
|
-
'button:has-text("Ajouter")',
|
|
3023
|
+
// Primary save buttons (bilingual via i18n)
|
|
3024
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'save'),
|
|
3025
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'done'),
|
|
3026
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'create'),
|
|
3027
|
+
...i18nSelectors('button:has-text("{text}")', 'buttons', 'add'),
|
|
3033
3028
|
// Icon buttons
|
|
3034
3029
|
'button:has(mat-icon:has-text("check"))',
|
|
3035
3030
|
'button:has(mat-icon:has-text("save"))',
|