diff --git a/.claude/worktrees/agent-a0d5a33d b/.claude/worktrees/agent-a0d5a33d new file mode 160000 index 0000000..3a24538 --- /dev/null +++ b/.claude/worktrees/agent-a0d5a33d @@ -0,0 +1 @@ +Subproject commit 3a245385cc0543b375cbf0ec2690ccb0ea3cd21d diff --git a/.claude/worktrees/agent-a1346262 b/.claude/worktrees/agent-a1346262 new file mode 160000 index 0000000..cacd60e --- /dev/null +++ b/.claude/worktrees/agent-a1346262 @@ -0,0 +1 @@ +Subproject commit cacd60e9d6ec7d815a7952d34c798703d68d2ddd diff --git a/.claude/worktrees/agent-a5fb028c b/.claude/worktrees/agent-a5fb028c new file mode 160000 index 0000000..cac2a1b --- /dev/null +++ b/.claude/worktrees/agent-a5fb028c @@ -0,0 +1 @@ +Subproject commit cac2a1bbd830f51a145bddcf97c5048bf5f4467a diff --git a/.claude/worktrees/agent-a84a8939 b/.claude/worktrees/agent-a84a8939 new file mode 160000 index 0000000..ae54b03 --- /dev/null +++ b/.claude/worktrees/agent-a84a8939 @@ -0,0 +1 @@ +Subproject commit ae54b031fa3f2473f2206a39dd081103d60dfa32 diff --git a/.claude/worktrees/agent-ad60cebb b/.claude/worktrees/agent-ad60cebb new file mode 160000 index 0000000..b0d26ce --- /dev/null +++ b/.claude/worktrees/agent-ad60cebb @@ -0,0 +1 @@ +Subproject commit b0d26ce0f51a6368f7a02e54e0a826445a831041 diff --git a/.claude/worktrees/agent-af145deb b/.claude/worktrees/agent-af145deb new file mode 160000 index 0000000..0d5ae80 --- /dev/null +++ b/.claude/worktrees/agent-af145deb @@ -0,0 +1 @@ +Subproject commit 0d5ae80fa32825c64d1dc836f54108b016f3f4c1 diff --git a/main.ts b/main.ts index 4933035..436f7f1 100644 --- a/main.ts +++ b/main.ts @@ -7,6 +7,7 @@ import { ConversionResult, DependencyStatus, PluginArgEntry, + TriedPath, } from './src/types/settings'; import { MarkitdownConverter } from './src/converter/MarkitdownConverter'; import { checkDependencies, installPackage } from './src/utils/python'; @@ -32,6 +33,7 @@ export default class MarkitdownPlugin extends Plugin { markitdownVersion: null, }; converter: MarkitdownConverter = new MarkitdownConverter('python', '.'); + pythonDiscoveryLog: TriedPath[] = []; private _resolvedPythonPath = 'python'; /** The Python path actually used after discovery/fallback resolution. */ @@ -45,6 +47,7 @@ export default class MarkitdownPlugin extends Plugin { const pluginDir = this.getPluginDir(); const depCheck = await checkDependencies(this.settings.pythonPath, pluginDir); this.dependencyStatus = depCheck.status; + this.pythonDiscoveryLog = depCheck.triedPaths; // Use the resolved python path (handles python→python3 fallback) this._resolvedPythonPath = depCheck.resolvedPythonPath; this.converter = new MarkitdownConverter(this.resolvedPythonPath, pluginDir); @@ -237,7 +240,7 @@ export default class MarkitdownPlugin extends Plugin { async installMarkitdown(onProgress?: (line: string) => void): Promise { const pluginDir = this.getPluginDir(); const success = await installPackage( - this.resolvedPythonPath, + this._resolvedPythonPath, pluginDir, 'markitdown[all]', onProgress @@ -253,6 +256,7 @@ export default class MarkitdownPlugin extends Plugin { const pluginDir = this.getPluginDir(); const depCheck = await checkDependencies(this.settings.pythonPath, pluginDir); this.dependencyStatus = depCheck.status; + this.pythonDiscoveryLog = depCheck.triedPaths; this._resolvedPythonPath = depCheck.resolvedPythonPath; this.converter = new MarkitdownConverter(this.resolvedPythonPath, pluginDir); } diff --git a/src/settings/SettingsTab.ts b/src/settings/SettingsTab.ts index 05e0ba5..deb4751 100644 --- a/src/settings/SettingsTab.ts +++ b/src/settings/SettingsTab.ts @@ -196,7 +196,23 @@ export class SettingsTab extends PluginSettingTab { dep.markitdownVersion ? `v${dep.markitdownVersion}` : undefined ); - // Install button + // Resolved path hint (when Python is found) + if (dep.pythonInstalled) { + const hint = statusContainer.createDiv('markitdown-resolved-path-hint'); + hint.setText(`Using: ${this.plugin.resolvedPythonPath}`); + } + + // ── Troubleshooting: Python NOT installed ── + if (!dep.pythonInstalled) { + this.renderPythonTroubleshooting(containerEl); + } + + // ── Troubleshooting: Python installed but markitdown missing ── + if (dep.pythonInstalled && !dep.markitdownInstalled) { + this.renderMarkitdownTroubleshooting(containerEl); + } + + // Install button (when Python exists but markitdown doesn't) if (!dep.markitdownInstalled && dep.pythonInstalled) { const installBtn = containerEl.createEl('button', { text: 'Install Markitdown', @@ -240,6 +256,86 @@ export class SettingsTab extends PluginSettingTab { }); } + private renderPythonTroubleshooting(containerEl: HTMLElement) { + const details = containerEl.createEl('details', { + cls: 'markitdown-troubleshooting', + }); + details.createEl('summary', { text: 'Troubleshooting: Python not found' }); + + const content = details.createDiv('markitdown-troubleshooting-content'); + + // Tried paths list + const log = this.plugin.pythonDiscoveryLog; + if (log.length > 0) { + content.createEl('p', { + text: 'The following paths were checked:', + cls: 'markitdown-troubleshooting-label', + }); + const list = content.createEl('ul', { cls: 'markitdown-tried-paths' }); + for (const entry of log) { + const li = list.createEl('li'); + const pathSpan = li.createEl('code', { text: entry.path }); + if (entry.error) { + li.createSpan({ + text: ` \u2014 ${entry.error}`, + cls: 'markitdown-tried-path-error', + }); + } + } + } + + // Download link + const downloadP = content.createEl('p'); + downloadP.createSpan({ text: 'Download Python from ' }); + downloadP.createEl('a', { + text: 'python.org', + href: 'https://www.python.org/downloads/', + }); + + // Platform-specific example path + const isWin = process.platform === 'win32'; + const examplePath = isWin + ? 'C:\\Python311\\python.exe' + : '/usr/local/bin/python3'; + content.createEl('p', { + text: `Example path for your platform: `, + cls: 'markitdown-troubleshooting-label', + }).createEl('code', { text: examplePath }); + } + + private renderMarkitdownTroubleshooting(containerEl: HTMLElement) { + const details = containerEl.createEl('details', { + cls: 'markitdown-troubleshooting', + }); + details.createEl('summary', { text: 'Troubleshooting: markitdown not installed' }); + + const content = details.createDiv('markitdown-troubleshooting-content'); + + content.createEl('p', { + text: 'Python was found, but the markitdown package is not installed. Run this command to install it:', + }); + + const cmdContainer = content.createDiv('markitdown-pip-command'); + const pipCmd = `${this.plugin.resolvedPythonPath} -m pip install "markitdown[all]"`; + cmdContainer.createEl('code', { text: pipCmd }); + + const copyBtn = cmdContainer.createEl('button', { + text: 'Copy', + cls: 'markitdown-copy-button', + }); + copyBtn.addEventListener('click', () => { + navigator.clipboard.writeText(pipCmd).then(() => { + copyBtn.setText('Copied!'); + setTimeout(() => copyBtn.setText('Copy'), 2000); + }); + }); + + content.createEl('p', { + text: 'Or use the "Install Markitdown" button below.', + cls: 'markitdown-troubleshooting-hint', + }); + } + private renderStatusItem( container: HTMLElement, label: string, diff --git a/src/types/settings.ts b/src/types/settings.ts index a2e1e8d..08e4139 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -49,3 +49,8 @@ export interface DependencyStatus { markitdownInstalled: boolean; markitdownVersion: string | null; } + +export interface TriedPath { + path: string; + error: string; +} diff --git a/src/utils/python.ts b/src/utils/python.ts index e084c11..d7d028e 100644 --- a/src/utils/python.ts +++ b/src/utils/python.ts @@ -1,6 +1,6 @@ import { spawn } from 'child_process'; import * as path from 'path'; -import { DependencyStatus } from '../types/settings'; +import { DependencyStatus, TriedPath } from '../types/settings'; interface PythonResult { stdout: string; @@ -70,7 +70,7 @@ export function getPythonScriptPath(scriptName: string, pluginDir: string): stri export async function checkDependencies( pythonPath: string, pluginDir: string -): Promise<{ status: DependencyStatus; resolvedPythonPath: string }> { +): Promise<{ status: DependencyStatus; resolvedPythonPath: string; triedPaths: TriedPath[] }> { const status: DependencyStatus = { pythonInstalled: false, pythonVersion: null, @@ -78,10 +78,13 @@ export async function checkDependencies( markitdownVersion: null, }; + const triedPaths: TriedPath[] = []; + // Guard against empty or whitespace-only paths const trimmed = pythonPath.trim(); if (!trimmed) { - return { status, resolvedPythonPath: pythonPath }; + triedPaths.push({ path: pythonPath || '(empty)', error: 'Path is empty or whitespace-only' }); + return { status, resolvedPythonPath: pythonPath, triedPaths }; } const scriptPath = getPythonScriptPath('check_install.py', pluginDir); @@ -153,22 +156,32 @@ export async function checkDependencies( // If this Python has markitdown, use it immediately if (depStatus.markitdownInstalled) { - return { status: depStatus, resolvedPythonPath: tryPath }; + triedPaths.push({ path: tryPath, error: '' }); + return { status: depStatus, resolvedPythonPath: tryPath, triedPaths }; } // Otherwise, remember the first working Python as a fallback + triedPaths.push({ path: tryPath, error: 'Python found but markitdown not installed' }); if (!firstWorkingPython) { firstWorkingPython = { status: depStatus, resolvedPythonPath: tryPath }; } + } else { + const detail = result.stderr || `exit code ${result.exitCode}`; + triedPaths.push({ path: tryPath, error: `Check script failed: ${detail}` }); } } catch (err) { + const errMsg = err instanceof Error ? err.message : String(err); + triedPaths.push({ path: tryPath, error: errMsg }); console.debug(`markitdown: ${tryPath} failed:`, err); continue; } } // No Python with markitdown found — return the first working Python (or the default) - return firstWorkingPython ?? { status, resolvedPythonPath: pythonPath }; + if (firstWorkingPython) { + return { ...firstWorkingPython, triedPaths }; + } + return { status, resolvedPythonPath: pythonPath, triedPaths }; } /** diff --git a/styles.css b/styles.css index 7ed28f6..2532999 100644 --- a/styles.css +++ b/styles.css @@ -176,6 +176,115 @@ Styles for the Markitdown Obsidian plugin font-weight: 500; } +/* Resolved path hint */ +.markitdown-resolved-path-hint { + font-size: 0.85em; + color: var(--text-muted); + margin-top: 4px; + font-family: var(--font-monospace); +} + +/* Troubleshooting section (collapsible) */ +.markitdown-troubleshooting { + margin: 12px 0; + border: 1px solid var(--background-modifier-border); + border-radius: 6px; + overflow: hidden; +} + +.markitdown-troubleshooting summary { + padding: 10px 14px; + cursor: pointer; + font-weight: 500; + color: var(--text-muted); + background-color: var(--background-secondary); + user-select: none; +} + +.markitdown-troubleshooting summary:hover { + color: var(--text-normal); +} + +.markitdown-troubleshooting-content { + padding: 12px 14px; +} + +.markitdown-troubleshooting-content p { + margin: 6px 0; + color: var(--text-muted); + font-size: 0.9em; +} + +.markitdown-troubleshooting-label { + font-weight: 500; + color: var(--text-normal); +} + +.markitdown-tried-paths { + list-style: none; + padding-left: 0; + margin: 6px 0; +} + +.markitdown-tried-paths li { + padding: 4px 0; + font-size: 0.85em; + color: var(--text-muted); + border-bottom: 1px solid var(--background-modifier-border); +} + +.markitdown-tried-paths li:last-child { + border-bottom: none; +} + +.markitdown-tried-paths code { + background-color: var(--background-primary-alt); + padding: 1px 4px; + border-radius: 3px; + font-size: 0.95em; +} + +.markitdown-tried-path-error { + color: var(--text-faint); +} + +.markitdown-pip-command { + display: flex; + align-items: center; + gap: 8px; + margin: 8px 0; + padding: 8px 12px; + background-color: var(--background-primary-alt); + border-radius: 4px; + font-family: var(--font-monospace); + font-size: 0.85em; +} + +.markitdown-pip-command code { + flex: 1; + word-break: break-all; +} + +.markitdown-copy-button { + flex-shrink: 0; + background-color: var(--interactive-accent); + color: var(--text-on-accent); + border: none; + padding: 4px 10px; + border-radius: 4px; + cursor: pointer; + font-size: 0.85em; +} + +.markitdown-copy-button:hover { + background-color: var(--interactive-accent-hover); +} + +.markitdown-troubleshooting-hint { + font-style: italic; + color: var(--text-faint); +} + /* Plugin args editor */ .markitdown-plugin-args-container { margin: 8px 0 16px;