agginti ulteriori fix per controllo obsidian

This commit is contained in:
gabriele-cusato 2026-03-29 00:00:06 +01:00
parent bcc689411b
commit b42451f65a
14 changed files with 44 additions and 26 deletions

View file

@ -58,8 +58,8 @@ export function registerEmbed(plugin: HandwritingPlugin) {
// Legge dimensioni reali dal viewBox per preservare l'altezza raggiunta con auto-expand.
// Usare plugin.settings.canvasHeight causerebbe il "collasso" degli SVG cresciuti.
const dimMatch = content.match(/viewBox="0 0 (\d+) (\d+)"/);
const svgWidth = dimMatch ? parseInt(dimMatch[1]!) : plugin.settings.canvasWidth;
const svgHeight = dimMatch ? parseInt(dimMatch[2]!) : plugin.settings.canvasHeight;
const svgWidth = dimMatch ? parseInt(dimMatch[1]) : plugin.settings.canvasWidth;
const svgHeight = dimMatch ? parseInt(dimMatch[2]) : plugin.settings.canvasHeight;
const newSvg = strokesToSvg(
remapped,
svgWidth,
@ -72,9 +72,9 @@ export function registerEmbed(plugin: HandwritingPlugin) {
plugin.refreshPreview(embedId, newSvg);
}
};
// eslint-disable-next-line @typescript-eslint/no-misused-promises
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- async listener stored in a Set that expects void; Promise result intentionally ignored
plugin.bgModeListeners.add(onBgModeRemap);
// eslint-disable-next-line @typescript-eslint/no-misused-promises
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- same reason: async callback passed to plugin.register cleanup
plugin.register(() => plugin.bgModeListeners.delete(onBgModeRemap));
// --- NUOVO: MutationObserver su document.body ---
@ -147,18 +147,15 @@ function setupMutationObserver(plugin: HandwritingPlugin) {
// Aggiorna l'altezza del wrapper (se espanso) dopo ogni refresh dell'img.
// Su Android, CM6 Live Preview non rileva i cambi organici di altezza.
const syncWrapperHeight = () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const wrapper = span.querySelector('.hwm_clip-wrapper') as HTMLElement | null;
const wrapper = span.querySelector<HTMLElement>('.hwm_clip-wrapper');
if (!wrapper || wrapper.classList.contains('hwm_overflow-hidden')) return;
const newH = wrapper.scrollHeight;
// eslint-disable-next-line obsidianmd/no-static-styles-assignment
wrapper.style.transition = 'none';
wrapper.style.height = newH + 'px';
// Disable transition temporarily so the height sync is instant (no visual flash).
wrapper.classList.add('hwm_no-transition');
wrapper.style.setProperty('height', newH + 'px');
requestAnimationFrame(() => {
// eslint-disable-next-line obsidianmd/no-static-styles-assignment
wrapper.style.height = '';
// eslint-disable-next-line obsidianmd/no-static-styles-assignment
wrapper.style.transition = '';
wrapper.style.removeProperty('height');
wrapper.classList.remove('hwm_no-transition');
});
};
@ -691,8 +688,7 @@ function createPortalPanel(
// Animiamo il wrapper (non il container span) così il ResizeObserver
// di Obsidian Mobile non si attiva sul container e non re-imposta le dimensioni dell'img.
const ensureWrapper = (): HTMLElement | null => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
let wrapper = container.querySelector('.hwm_clip-wrapper') as HTMLElement | null;
let wrapper = container.querySelector<HTMLElement>('.hwm_clip-wrapper');
if (wrapper) return wrapper;
const img = container.querySelector('img');
if (!img || !img.parentElement) return null;
@ -710,15 +706,14 @@ function createPortalPanel(
const wrapper = ensureWrapper();
if (wrapper) {
// Anima da altezza corrente (px) → altezza piena (scrollHeight).
wrapper.style.height = wrapper.scrollHeight + 'px';
wrapper.style.setProperty('height', wrapper.scrollHeight + 'px');
// Cleanup: rimuove altezza fissa e overflow al termine dell'animazione.
// Su Android WebView transitionend non è affidabile → setTimeout fallback.
let done = false;
const cleanup = () => {
if (done) return;
done = true;
// eslint-disable-next-line obsidianmd/no-static-styles-assignment
wrapper.style.height = '';
wrapper.style.removeProperty('height');
wrapper.classList.remove('hwm_overflow-hidden');
};
wrapper.addEventListener('transitionend', cleanup, { once: true });
@ -748,7 +743,7 @@ function createPortalPanel(
wrapper.classList.add('hwm_overflow-hidden');
if (startH <= naturalH) {
// SVG non espanso: altezza già nella norma, nessuna area vuota da tagliare.
wrapper.style.height = startH + 'px';
wrapper.style.setProperty('height', startH + 'px');
} else {
// SVG auto-espanso: anima da startH → naturalH per nascondere l'area vuota.
// WAAPI garantisce keyframe px→px senza dipendere da height:auto come "from".
@ -757,7 +752,7 @@ function createPortalPanel(
{ duration: 300, easing: 'ease', fill: 'forwards' }
);
anim.onfinish = () => {
wrapper.style.height = naturalH + 'px';
wrapper.style.setProperty('height', naturalH + 'px');
anim.cancel(); // cede il controllo all'inline style
};
}

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "Erforderlich für die OCR-Handschrifterkennung. Erhalten Sie ihn von Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "OCR-Sprachen",
"ocr_langs_desc": "Kommagetrennte BCP-47-Sprachcodes (z. B. \"it, en, fr\"). Wird vom KI-Modell für OCR verwendet.",
"gemini_key_placeholder": "API-Schlüssel eingeben",
"ocr_langs_placeholder": "z. B. it, en, fr",
"debug_mode_name": "Debug-Modus",
"debug_mode_desc": "Echtzeit-Benachrichtigungen für IME/Touch-Ereignisse anzeigen (nützlich zur Diagnose von Android-Problemen).",
"keywords_summary": "Vom OCR-Parser erkannte Schlüsselwörter",

View file

@ -14,6 +14,8 @@
"gemini_key_desc": "Required for handwriting OCR recognition. Get it from Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "OCR languages",
"ocr_langs_desc": "Comma-separated BCP-47 language codes (e.g. \"it, en, fr\"). Used by the AI model for OCR.",
"gemini_key_placeholder": "Enter API key",
"ocr_langs_placeholder": "e.g. it, en, fr",
"debug_mode_name": "Debug mode",
"debug_mode_desc": "Show real-time notifications for IME/touch events (useful for diagnosing issues on Android).",
"keywords_summary": "Keywords recognized by the OCR parser",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "Necesaria para el reconocimiento OCR de escritura a mano. Obténgala en Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "Idiomas OCR",
"ocr_langs_desc": "Códigos de idioma BCP-47 separados por comas (p. ej. \"it, en, fr\"). Usados por el modelo de IA para el OCR.",
"gemini_key_placeholder": "Introduce la clave API",
"ocr_langs_placeholder": "p. ej. it, en, fr",
"debug_mode_name": "Modo depuración",
"debug_mode_desc": "Muestra notificaciones en tiempo real para eventos IME/táctil (útil para diagnosticar problemas en Android).",
"keywords_summary": "Palabras clave reconocidas por el parser OCR",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "Nécessaire pour la reconnaissance OCR de l'écriture manuscrite. Obtenez-la sur Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "Langues OCR",
"ocr_langs_desc": "Codes de langue BCP-47 séparés par des virgules (ex. : \"it, en, fr\"). Utilisés par le modèle IA pour l'OCR.",
"gemini_key_placeholder": "Saisir la clé API",
"ocr_langs_placeholder": "ex. it, en, fr",
"debug_mode_name": "Mode débogage",
"debug_mode_desc": "Affiche des notifications en temps réel pour les événements IME/tactile (utile pour diagnostiquer les problèmes sur Android).",
"keywords_summary": "Mots-clés reconnus par le parser OCR",

View file

@ -14,6 +14,8 @@
"gemini_key_desc": "Necessaria per il riconoscimento OCR della scrittura a mano. Ottienila da Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "Lingue OCR",
"ocr_langs_desc": "Codici lingua BCP-47 separati da virgola (es. \"it, en, fr\"). Usati dal modello AI per utilizzare l'OCR.",
"gemini_key_placeholder": "Inserisci la chiave API",
"ocr_langs_placeholder": "es. it, en, fr",
"debug_mode_name": "Modalità debug",
"debug_mode_desc": "Mostra notifiche in tempo reale per eventi IME/touch (utile per diagnosticare problemi su Android).",
"keywords_summary": "Keyword riconosciute dal parser OCR",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "手書きOCR認識に必要です。Google AI Studioaistudio.google.comから取得してください。",
"ocr_langs_name": "OCR言語",
"ocr_langs_desc": "カンマ区切りのBCP-47言語コード「it, en, fr」。AIモデルのOCRに使用されます。",
"gemini_key_placeholder": "APIキーを入力",
"ocr_langs_placeholder": "例it, en, fr",
"debug_mode_name": "デバッグモード",
"debug_mode_desc": "IME/タッチイベントのリアルタイム通知を表示しますAndroidの問題診断に役立ちます。",
"keywords_summary": "OCRパーサーが認識するキーワード",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "Wymagany do rozpoznawania OCR pisma odręcznego. Uzyskaj go w Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "Języki OCR",
"ocr_langs_desc": "Kody języków BCP-47 oddzielone przecinkami (np. \"it, en, fr\"). Używane przez model AI do OCR.",
"gemini_key_placeholder": "Wprowadź klucz API",
"ocr_langs_placeholder": "np. it, en, fr",
"debug_mode_name": "Tryb debugowania",
"debug_mode_desc": "Pokazuje powiadomienia w czasie rzeczywistym dla zdarzeń IME/dotyk (przydatne do diagnozowania problemów na Androidzie).",
"keywords_summary": "Słowa kluczowe rozpoznawane przez parser OCR",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "Necessária para o reconhecimento OCR de manuscrito. Obtenha no Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "Idiomas OCR",
"ocr_langs_desc": "Códigos de idioma BCP-47 separados por vírgula (ex: \"it, en, fr\"). Usados pelo modelo de IA para OCR.",
"gemini_key_placeholder": "Insira a chave de API",
"ocr_langs_placeholder": "ex.: it, en, fr",
"debug_mode_name": "Modo debug",
"debug_mode_desc": "Mostra notificações em tempo real para eventos IME/toque (útil para diagnosticar problemas no Android).",
"keywords_summary": "Palavras-chave reconhecidas pelo parser OCR",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "Необходим для OCR-распознавания рукописного текста. Получите в Google AI Studio (aistudio.google.com).",
"ocr_langs_name": "Языки OCR",
"ocr_langs_desc": "Коды языков BCP-47, разделённые запятой (например \"it, en, fr\"). Используются моделью ИИ для OCR.",
"gemini_key_placeholder": "Введите API-ключ",
"ocr_langs_placeholder": "напр. it, en, fr",
"debug_mode_name": "Режим отладки",
"debug_mode_desc": "Показывать уведомления в реальном времени для событий IME/касания (полезно для диагностики проблем на Android).",
"keywords_summary": "Ключевые слова, распознаваемые парсером OCR",

View file

@ -21,6 +21,8 @@
"gemini_key_desc": "手写OCR识别所需。从Google AI Studio (aistudio.google.com)获取。",
"ocr_langs_name": "OCR语言",
"ocr_langs_desc": "逗号分隔的BCP-47语言代码例如\"it, en, fr\"。用于AI模型的OCR识别。",
"gemini_key_placeholder": "输入API密钥",
"ocr_langs_placeholder": "例如it, en, fr",
"debug_mode_name": "调试模式",
"debug_mode_desc": "显示IME/触摸事件的实时通知用于诊断Android问题。",
"keywords_summary": "OCR解析器识别的关键词",

View file

@ -17,7 +17,7 @@
export function normalizeMarkdownSymbols(rawText: string): string {
// Rimuove BOM e caratteri zero-width Unicode che Gemini inserisce all'inizio
// del testo o all'inizio di righe (U+FEFF, U+200B, U+200C, U+200D, U+2060)
// eslint-disable-next-line no-misleading-character-class
// eslint-disable-next-line no-misleading-character-class -- intentional: each codepoint in this class is a distinct zero-width character, not a combining sequence
const cleaned = rawText.replace(/[\uFEFF\u200B\u200C\u200D\u2060]/gu, '');
const lines = cleaned.split('\n');

View file

@ -121,7 +121,7 @@ export class HandwritingSettingTab extends PluginSettingTab {
// Prima voce: automatico
drop.addOption('auto', t('ui_language_auto'));
// Una voce per ogni lingua disponibile nel plugin, con nome nativo
// eslint-disable-next-line @typescript-eslint/no-misused-promises
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- forEach callback is sync but DropdownComponent.addOption return type triggers the rule; Promise result intentionally ignored
availableLocales().forEach(code => drop.addOption(code, localeNames[code] ?? code));
drop.setValue(this.plugin.settings.uiLanguage);
drop.onChange((value) => { void (async () => {
@ -195,8 +195,7 @@ export class HandwritingSettingTab extends PluginSettingTab {
.setDesc(t('gemini_key_desc'))
.addText(text => {
text
// eslint-disable-next-line obsidianmd/ui/sentence-case
.setPlaceholder('AIza...')
.setPlaceholder(t('gemini_key_placeholder'))
.setValue(this.plugin.settings.geminiApiKey)
.onChange(async (value) => {
this.plugin.settings.geminiApiKey = value.trim();
@ -213,8 +212,7 @@ export class HandwritingSettingTab extends PluginSettingTab {
.addText(text => text
// Mostra l'array come stringa "it, en"
.setValue(this.plugin.settings.ocrLanguages.join(', '))
// eslint-disable-next-line obsidianmd/ui/sentence-case
.setPlaceholder('it, en')
.setPlaceholder(t('ocr_langs_placeholder'))
.onChange(async (value) => {
// Parsa la stringa in array, rimuovendo spazi e voci vuote
const langs = value

View file

@ -22,6 +22,11 @@
overflow: hidden;
}
/* Disabilita transizioni CSS temporaneamente (usato in syncWrapperHeight) */
.hwm_no-transition {
transition: none !important;
}
/* Animazione altezza del wrapper clip (collapse/expand nuovo formato) */
.hwm_clip-wrapper {
transition: height 0.3s ease;