/** * Central selector registry for the NotebookLM web UI. * * # Multilingual strategy * * Google ships NotebookLM in dozens of locales. Anchor priority: * * 1. **Class names** (`.add-source-button`, `.single-source-container`, * `.submit-button`, `.create-artifact-button-container`, …) — these * are Angular component selectors and identical in every locale. * * 2. **Material-Symbols icon names** (`audio_magic_eraser`, `content_paste`, * `link`, `upload`, `download`, …) — Google ships them as the literal * text node of `` in every locale, so they are 100% language- * agnostic. Most stable anchor for icon-driven controls. * * 3. **`role="dialog"`, `role="button"`** — set synchronously by Angular, * no animation race. * * 4. **Locale-bound aria-labels and visible text** — last resort. Each * list below covers the eight major NotebookLM locales: * EN, DE, FR, ES, PT, IT, NL, JA. Adding more is mechanical; nothing * breaks if a locale is missing because the class/icon anchors fire * first. * * Last verified: 2026-05 against the live notebooklm.google.com layout * (DE, EN locales). */ export declare const Selectors: { readonly chat: { readonly answerContainer: ".to-user-container"; readonly answerText: ".to-user-container .message-text-content"; readonly latestAnswerText: ".to-user-container:last-child .message-text-content"; /** * Chat textarea. The class is shared across locales; aria-labels are a * fallback for older builds where the class was different. */ readonly queryInput: readonly ["textarea.query-box-input", "textarea[aria-label*=\"query\" i]", "textarea[aria-label*=\"anfrag\" i]", "textarea[aria-label*=\"requete\" i]", "textarea[aria-label*=\"zone de requete\" i]", "textarea[aria-label*=\"consulta\" i]", "textarea[aria-label*=\"domanda\" i]", "textarea[aria-label*=\"vraag\" i]", "textarea[aria-label*=\"質問\" i]", "textarea[aria-label*=\"pergunta\" i]"]; /** * The chat submit button has the *language-bound* aria-label * (Send / Senden / Envoyer / Enviar / Invia / Verzenden / 送信). It also * has the stable class `.submit-button`. The sources web-search overlay * uses `.actions-enter-button` with the SAME aria-label, so we MUST * anchor on `.submit-button` to avoid distractor matches. */ readonly submitButton: readonly ["button.submit-button", "button.submit-button[aria-label*=\"send\" i]", "button.submit-button[aria-label*=\"senden\" i]", "button.submit-button[aria-label*=\"envoyer\" i]", "button.submit-button[aria-label*=\"enviar\" i]", "button.submit-button[aria-label*=\"invia\" i]", "button.submit-button[aria-label*=\"verzend\" i]", "button.submit-button[aria-label*=\"送信\" i]"]; }; /** * NotebookLM removed tabs in favour of a three-pane sidebar (2026 layout). * These selectors are kept only for the rare legacy layouts. */ readonly tabs: { readonly discussion: readonly ["[role=\"tab\"]:has-text(\"Discussion\")", "[role=\"tab\"]:has-text(\"Diskussion\")", "[role=\"tab\"]:has-text(\"Diskussionen\")", "[role=\"tab\"]:has-text(\"Discusión\")", "[role=\"tab\"]:has-text(\"Discussione\")", "[role=\"tab\"]:has-text(\"Discussão\")", "[role=\"tab\"]:has-text(\"ディスカッション\")"]; readonly sources: readonly ["[role=\"tab\"]:has-text(\"Sources\")", "[role=\"tab\"]:has-text(\"Quellen\")", "[role=\"tab\"]:has-text(\"Fuentes\")", "[role=\"tab\"]:has-text(\"Fonti\")", "[role=\"tab\"]:has-text(\"Fontes\")", "[role=\"tab\"]:has-text(\"Bronnen\")", "[role=\"tab\"]:has-text(\"ソース\")"]; readonly activeTabClass: "mdc-tab--active"; readonly tabList: ".mat-mdc-tab-list .mdc-tab"; }; readonly citations: { readonly button: readonly ["button.citation-marker", "button.xap-inline-dialog.citation-marker", "button[data-citation]"]; readonly label: "span[aria-label]"; readonly highlight: ".highlighted"; readonly paragraph: ".paragraph"; readonly paragraphHighlight: ".paragraph .highlighted"; }; readonly sources: { /** * Per-source row in the sidebar (language-agnostic). Stable Angular * class — verified across all observed locales. */ readonly sourceContainer: ".single-source-container"; /** * "X Quellen" / "X sources" header text. Numeric so we read the count * via regex on the visible text. Independent of sidebar collapse state. */ readonly sourceCountIndicator: ".cover-subtitle-source-count"; /** * Sidebar "Add source" button. Class `.add-source-button` is language- * agnostic; aria-labels listed for older builds without the class. */ readonly addButton: readonly ["button.add-source-button", "button[aria-label=\"Add source\"]", "button[aria-label*=\"add source\" i]", "button[aria-label*=\"quelle hinzu\" i]", "button[aria-label*=\"ajouter une source\" i]", "button[aria-label*=\"añadir fuente\" i]", "button[aria-label*=\"agregar fuente\" i]", "button[aria-label*=\"aggiungi fonte\" i]", "button[aria-label*=\"adicionar fonte\" i]", "button[aria-label*=\"bron toevoegen\" i]", "button[aria-label*=\"ソースを追加\" i]"]; /** * Real Material modal. `[role="dialog"]` is set by Angular synchronously * the moment the modal mounts — race-free against the `.mdc-dialog--open` * animation class and resistant to Material-UI version bumps. Avoid * `.cdk-overlay-pane` (matches every dropdown / emoji picker / menu). */ readonly overlayPane: "[role=\"dialog\"]"; readonly overlayInput: "[role=\"dialog\"] input[type=\"text\"]:not([readonly])"; readonly overlayTextarea: "[role=\"dialog\"] textarea"; /** * Source-type buttons in the Add-source overlay. Google ships them * *without* aria-labels — the only stable, language-agnostic anchor is * the Material-Symbols icon name baked into a `` text node. */ readonly sourceTypeUrl: readonly ["button.drop-zone-icon-button:has(mat-icon.youtube-icon)", "button.drop-zone-icon-button:has(mat-icon:text-is(\"link\"))", "button.drop-zone-icon-button:has-text(\"Websites\")", "button.drop-zone-icon-button:has-text(\"Website\")", "button.drop-zone-icon-button:has-text(\"Sites Web\")", "button.drop-zone-icon-button:has-text(\"Sitio web\")", "button.drop-zone-icon-button:has-text(\"Sito web\")", "button.drop-zone-icon-button:has-text(\"Sites\")", "button.drop-zone-icon-button:has-text(\"ウェブサイト\")", "span:has-text(\"Website\")", "span:has-text(\"URL\")"]; readonly sourceTypeText: readonly ["button.drop-zone-icon-button:has(mat-icon:text-is(\"content_paste\"))", "button.drop-zone-icon-button:has-text(\"Kopierter Text\")", "button.drop-zone-icon-button:has-text(\"Copied text\")", "button.drop-zone-icon-button:has-text(\"Pasted text\")", "button.drop-zone-icon-button:has-text(\"Texte copié\")", "button.drop-zone-icon-button:has-text(\"Texto copiado\")", "button.drop-zone-icon-button:has-text(\"Testo copiato\")", "button.drop-zone-icon-button:has-text(\"Gekopieerde tekst\")", "button.drop-zone-icon-button:has-text(\"コピーしたテキスト\")", "span:has-text(\"Copied text\")", "span:has-text(\"Pasted text\")", "[data-type=\"text\"]"]; readonly sourceTypeYoutube: readonly ["button.drop-zone-icon-button mat-icon.youtube-icon", "button.drop-zone-icon-button:has(mat-icon:text-is(\"video_youtube\"))"]; readonly sourceTypeFile: readonly ["input[type=\"file\"]", "button.drop-zone-icon-button:has(mat-icon:text-is(\"upload\"))", "button.drop-zone-icon-button:has-text(\"Dateien hochladen\")", "button.drop-zone-icon-button:has-text(\"Upload sources\")", "button.drop-zone-icon-button:has-text(\"Importer\")", "button.drop-zone-icon-button:has-text(\"Subir\")", "button.drop-zone-icon-button:has-text(\"Carica\")", "button.drop-zone-icon-button:has-text(\"Uploaden\")", "button.drop-zone-icon-button:has-text(\"アップロード\")"]; /** * Primary submit button in the add-source dialog. Material's * `.mdc-button--raised` class is the most stable anchor; per-locale * visible-text variants are fallbacks for older builds. */ readonly insertConfirm: readonly ["button.mdc-button--raised:has-text(\"Insert\")", "button.mat-flat-button:has-text(\"Insert\")", "button[color=\"primary\"]:has-text(\"Insert\")", "button.mdc-button--raised:has-text(\"Einfügen\")", "button.mdc-button--raised:has-text(\"Hinzufügen\")", "button.mdc-button--raised:has-text(\"Ajouter\")", "button.mdc-button--raised:has-text(\"Insertar\")", "button.mdc-button--raised:has-text(\"Inserisci\")", "button.mdc-button--raised:has-text(\"Invoegen\")", "button.mdc-button--raised:has-text(\"挿入\")", "button:has-text(\"Insert\")", "button:has-text(\"Einfügen\")", "button:has-text(\"Hinzufügen\")", "button:has-text(\"Ajouter\")", "button:has-text(\"Insérer\")", "button:has-text(\"Insertar\")", "button:has-text(\"Añadir\")", "button:has-text(\"Agregar\")", "button:has-text(\"Inserisci\")", "button:has-text(\"Aggiungi\")", "button:has-text(\"Inserir\")", "button:has-text(\"Adicionar\")", "button:has-text(\"Invoegen\")", "button:has-text(\"Toevoegen\")", "button:has-text(\"挿入\")", "button:has-text(\"追加\")", "button:has-text(\"Add\")", "button:has-text(\"Submit\")", "button[type=\"submit\"]", "[role=\"dialog\"] .mdc-dialog__actions button:not(:has-text(\"Cancel\")):not(:has-text(\"Close\")):not(:has-text(\"Schließen\")):not(:has-text(\"Annuler\")):not(:has-text(\"Cancelar\")):not(:has-text(\"Annulla\")):not(:has-text(\"Annuleren\")):not(:has-text(\"キャンセル\"))"]; }; readonly studio: { /** * "Audio Overview" entry control. As of the 2026-05 Studio layout this * is a `
` with a Material-Symbols `audio_magic_eraser` * icon, NOT a real `