Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "joplin-plugin-note-categorization",
"version": "0.1.11",
"version": "0.1.12",
"scripts": {
"dist": "webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && npm run copyAssets && webpack --env joplin-plugin-config=createArchive",
"prepare": "npm run dist",
Expand Down
2 changes: 1 addition & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 1,
"id": "com.harsh16gupta.notecategorization",
"app_min_version": "3.5",
"version": "0.1.11",
"version": "0.1.12",
"name": "Note Categorization Plugin",
"description": "AI-based note categorisation: clusters notes semantically, suggests tags and notebook structures, and detects stale notes.",
"author": "Harsh Gupta",
Expand Down
11 changes: 11 additions & 0 deletions src/panel/setupPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ export async function setupPanel(operationState: OperationState): Promise<string
await joplin.views.panels.onMessage(panel, async (msg: WebviewMessage) => {
switch (msg.type) {
case 'run': {
if (operationState.inProgress) {
return panelState;
}
operationState.inProgress = true;
panelState = { type: 'status', text: 'Starting pipeline...' };
log('Panel: starting pipeline');

Expand All @@ -59,6 +63,7 @@ export async function setupPanel(operationState: OperationState): Promise<string
panelState = { type: 'progress', current, total, cached, skipped, isNativeAiUsed };
},
onComplete: (strategies, notes, isNativeAiUsed, isAiNamingUsed) => {
operationState.inProgress = false;
lastResultsState = {
strategies,
notes,
Expand All @@ -69,6 +74,7 @@ export async function setupPanel(operationState: OperationState): Promise<string
panelState = { type: 'results', strategies, notes, isNativeAiUsed, isAiNamingUsed };
},
onError: (message) => {
operationState.inProgress = false;
panelState = { type: 'error', message };
},
},
Expand Down Expand Up @@ -106,6 +112,11 @@ export async function setupPanel(operationState: OperationState): Promise<string
panelState: isApplyOrUndo ? panelState : undefined,
};
}
// Safety net: if panelState appears idle but a pipeline is still running,
// return a synthetic status so the webview starts polling and recovers.
if (operationState.inProgress) {
return { type: 'status', text: 'Processing...' };
}
return panelState;

case 'syncState':
Expand Down
34 changes: 34 additions & 0 deletions src/webview/context/AppStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,40 @@ export const AppStateProvider: React.FC<{ children: React.ReactNode }> = ({ chil
) {
startPolling();
}
} else {
// IPC may have failed under heavy load (e.g., during model loading
// or embedding). Start a brief recovery poll to check if a pipeline
// is actually running in the background.
let recoveryAttempts = 0;
const MAX_RECOVERY_ATTEMPTS = 5;
const recoveryInterval = setInterval(async () => {
recoveryAttempts++;
try {
const state = await webviewApi.postMessage<PanelMessage | { type: 'idle' }>({
type: 'poll',
});
if (state && state.type !== 'idle') {
clearInterval(recoveryInterval);
handlePollResponseRef.current(state);
if (
state.type === 'status' ||
state.type === 'progress' ||
state.type === 'apply_status' ||
state.type === 'apply_progress' ||
state.type === 'undo_status' ||
state.type === 'undo_progress'
) {
startPolling();
}
} else if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) {
clearInterval(recoveryInterval);
}
} catch {
if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) {
clearInterval(recoveryInterval);
}
}
}, 1000);
}
})
.catch((err) => {
Expand Down
Loading