Merge branch 'worktree-agent-ad60cebb'

# Conflicts:
#	main.ts
This commit is contained in:
Ethan Troy 2026-03-17 20:39:48 -04:00
commit fa48a94b98
11 changed files with 240 additions and 7 deletions

@ -0,0 +1 @@
Subproject commit 3a245385cc0543b375cbf0ec2690ccb0ea3cd21d

@ -0,0 +1 @@
Subproject commit cacd60e9d6ec7d815a7952d34c798703d68d2ddd

@ -0,0 +1 @@
Subproject commit cac2a1bbd830f51a145bddcf97c5048bf5f4467a

@ -0,0 +1 @@
Subproject commit ae54b031fa3f2473f2206a39dd081103d60dfa32

@ -0,0 +1 @@
Subproject commit b0d26ce0f51a6368f7a02e54e0a826445a831041

@ -0,0 +1 @@
Subproject commit 0d5ae80fa32825c64d1dc836f54108b016f3f4c1

View file

@ -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<boolean> {
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);
}

View file

@ -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,

View file

@ -49,3 +49,8 @@ export interface DependencyStatus {
markitdownInstalled: boolean;
markitdownVersion: string | null;
}
export interface TriedPath {
path: string;
error: string;
}

View file

@ -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 };
}
/**

View file

@ -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;