chore(web): remove dead legacy client-side plugin-config generator (~2,300 lines)

Plugin config forms have been rendered server-side (plugin_config.html via
GET /partials/plugin-config/<id>) since the HTMX migration; the old
client-side generator survived as unreachable code. Verified dead by call
graph, not by naming: showPluginConfigModal and showGithubTokenInstructions
have zero callers anywhere in templates or JS, and everything removed here
is reachable only from those two roots.

Removed:
- plugins_manager.js: showPluginConfigModal, generatePluginConfigForm,
  generateFormFromSchema, generateFieldHtml, generateSimpleConfigForm,
  handlePluginConfigSubmit, the modal's JSON-editor view (initJsonEditor,
  switchPluginConfigView, syncFormToJson/JsonToForm, saveConfigFromJsonEditor,
  resetPluginConfigToDefaults, displayValidationErrors, closePluginConfigModal,
  savePluginConfiguration, currentPluginConfigState), their exclusive helpers
  (getSchemaPropertyType, escapeCssSelector, dotToNested, collectBooleanFields,
  normalizeFormDataForConfig, flattenConfig, loadCustomHtmlWidget), the
  orphaned-modal cleanup block, the modal's listener wiring, and the
  never-invoked showGithubTokenInstructions/closeInstructionsModal pair.
- plugins.html: the #plugin-config-modal markup those functions drove.
- base.html: the deprecated pluginConfigData() component and the
  window.PluginConfigHelpers shim (only ever called by pluginConfigData).

Deliberately kept, verified still live:
- renderArrayObjectItem, getSchemaProperty, escapeHtml/escapeAttribute
  (window-exposed for the top-level array-of-objects handlers the
  server-rendered form uses), toggleNestedSection, addKeyValuePair/
  addArrayObjectItem families, executePluginAction, and
  window.currentPluginConfig = null init (file-upload.js and
  executePluginAction read it, optional-chained).
- app()'s internal generateConfigForm/generateSimpleConfigForm methods in
  base.html: unreachable now but embedded in the live Alpine component;
  excising methods from a live object is deferred to keep this change
  zero-risk.

Validation: every deletion seam inspected line-by-line; Jinja parse of both
templates passes; repo-wide sweep confirms zero remaining references to any
deleted function or element id (deleted ranges contained no Jinja tags).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
This commit is contained in:
ChuckBuilds
2026-07-16 09:02:16 -04:00
co-authored by Claude Sonnet 5
parent 66f9950a30
commit d2b49c4ff2
3 changed files with 0 additions and 2289 deletions
-338
View File
@@ -1676,216 +1676,6 @@
}
});
// ===== DEPRECATED: pluginConfigData =====
// This function is no longer used - plugin configuration forms are now
// rendered server-side and loaded via HTMX. Kept for backwards compatibility.
// See: /v3/partials/plugin-config/<plugin_id> for the new implementation.
function pluginConfigData(plugin) {
if (!plugin) {
console.error('pluginConfigData called with undefined plugin');
return {
plugin: { id: 'unknown', name: 'Unknown Plugin', enabled: false },
loading: false,
config: {},
schema: {},
webUiActions: [],
onDemandRefreshing: false,
onDemandStopping: false
};
}
return {
plugin: plugin,
loading: true,
config: {},
schema: {},
webUiActions: [],
onDemandRefreshing: false,
onDemandStopping: false,
get onDemandStore() {
if (window.Alpine && typeof Alpine.store === 'function' && Alpine.store('onDemand')) {
return Alpine.store('onDemand');
}
return window.__onDemandStore || { loading: true, state: {}, service: {}, error: null, lastUpdated: null };
},
get isOnDemandLoading() {
const store = this.onDemandStore || {};
return !!store.loading;
},
get onDemandState() {
const store = this.onDemandStore || {};
return store.state || {};
},
get onDemandService() {
const store = this.onDemandStore || {};
return store.service || {};
},
get onDemandError() {
const store = this.onDemandStore || {};
return store.error || null;
},
get onDemandActive() {
const state = this.onDemandState;
return !!(state.active && state.plugin_id === plugin.id);
},
resolvePluginName() {
return plugin.name || plugin.id;
},
resolvePluginDisplayName(id) {
if (!id) {
return 'Another plugin';
}
const list = window.installedPlugins || [];
const match = Array.isArray(list) ? list.find(p => p.id === id) : null;
return match ? (match.name || match.id) : id;
},
formatDuration(value) {
if (value === undefined || value === null) {
return '';
}
const total = Number(value);
if (Number.isNaN(total)) {
return '';
}
const seconds = Math.max(0, Math.round(total));
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
if (minutes > 0) {
return `${minutes}m${remainingSeconds > 0 ? ` ${remainingSeconds}s` : ''}`;
}
return `${remainingSeconds}s`;
},
get onDemandStatusText() {
if (this.isOnDemandLoading) {
return 'Loading on-demand status...';
}
if (this.onDemandError) {
return `On-demand error: ${this.onDemandError}`;
}
const state = this.onDemandState;
if (state.active) {
const activeName = this.resolvePluginDisplayName(state.plugin_id);
if (state.plugin_id !== plugin.id) {
return `${activeName} is running on-demand.`;
}
const modeLabel = state.mode ? ` (${state.mode})` : '';
const remaining = this.formatDuration(state.remaining);
const duration = this.formatDuration(state.duration);
let message = `${this.resolvePluginName()}${modeLabel} is running on-demand`;
if (remaining) {
message += ` — ${remaining} remaining`;
} else if (duration) {
message += ` — duration ${duration}`;
} else {
message += ' — until stopped';
}
return message;
}
const lastEvent = state.last_event ? state.last_event.replace(/-/g, ' ') : null;
if (lastEvent && lastEvent !== 'cleared') {
return `No on-demand session active (last event: ${lastEvent})`;
}
return 'No on-demand session active.';
},
get onDemandStatusClass() {
if (this.isOnDemandLoading) return 'text-blue-600';
if (this.onDemandError) return 'text-red-600';
if (this.onDemandActive) return 'text-green-600';
return 'text-blue-600';
},
get onDemandServiceText() {
if (this.isOnDemandLoading) {
return 'Checking display service status...';
}
if (this.onDemandError) {
return 'Display service status unavailable.';
}
if (this.onDemandService.active) {
return 'Display service is running.';
}
const serviceError = this.onDemandService.stderr || this.onDemandService.error;
return serviceError ? `Display service inactive (${serviceError})` : 'Display service is not running.';
},
get onDemandServiceClass() {
if (this.isOnDemandLoading) return 'text-blue-500';
if (this.onDemandError) return 'text-red-500';
return this.onDemandService.active ? 'text-blue-500' : 'text-red-500';
},
get onDemandLastUpdated() {
const store = this.onDemandStore || {};
if (!store.lastUpdated) {
return '';
}
const deltaSeconds = Math.round((Date.now() - store.lastUpdated) / 1000);
if (deltaSeconds < 5) return 'Just now';
if (deltaSeconds < 60) return `${deltaSeconds}s ago`;
const minutes = Math.round(deltaSeconds / 60);
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.round(minutes / 60);
return `${hours}h ago`;
},
get canStopOnDemand() {
if (this.isOnDemandLoading) return false;
if (this.onDemandError) return true;
return this.onDemandActive;
},
get disableRunButton() {
return !plugin.enabled;
},
get showEnableHint() {
return !plugin.enabled;
},
notify(message, type = 'info') {
if (typeof showNotification === 'function') {
showNotification(message, type);
}
},
refreshOnDemandStatus() {
if (typeof window.loadOnDemandStatus !== 'function') {
this.notify('On-demand status controls unavailable. Refresh the Plugin Manager tab.', 'error');
return;
}
this.onDemandRefreshing = true;
Promise.resolve(window.loadOnDemandStatus(true))
.finally(() => {
this.onDemandRefreshing = false;
});
},
runOnDemand() {
// Note: On-demand can work with disabled plugins - the backend will temporarily enable them
if (typeof window.openOnDemandModal === 'function') {
window.openOnDemandModal(plugin.id);
} else {
this.notify('On-demand modal unavailable. Refresh the Plugin Manager tab.', 'error');
}
},
stopOnDemandWithEvent(stopService = false) {
if (typeof window.requestOnDemandStop !== 'function') {
this.notify('Unable to stop on-demand mode. Refresh the Plugin Manager tab.', 'error');
return;
}
this.onDemandStopping = true;
Promise.resolve(window.requestOnDemandStop({ stopService }))
.finally(() => {
this.onDemandStopping = false;
});
},
async loadPluginConfig(pluginId) {
// Use PluginConfigHelpers to load config directly into this component
if (window.PluginConfigHelpers) {
await window.PluginConfigHelpers.loadPluginConfig(pluginId, this);
this.loading = false;
return;
}
console.error('loadPluginConfig not available');
this.loading = false;
}
// Note: generateConfigForm and savePluginConfig are now called via window.PluginConfigHelpers
// to avoid delegation recursion and ensure proper access to app component.
// See template usage:
// x-html="window.PluginConfigHelpers.generateConfigForm(...)" and
// x-on:submit.prevent="window.PluginConfigHelpers.savePluginConfig(...)"
};
}
// Alpine.js app function - full implementation
function app() {
@@ -3399,134 +3189,6 @@
// Make app() available globally
window.app = app;
// ===== DEPRECATED: Plugin Configuration Functions (Global Access) =====
// These functions are no longer the primary method for loading plugin configs.
// Plugin configuration forms are now rendered server-side via HTMX.
// See: /v3/partials/plugin-config/<plugin_id> for the new implementation.
// Kept for backwards compatibility with any remaining client-side code.
window.PluginConfigHelpers = {
loadPluginConfig: async function(pluginId, componentContext) {
// This function can be called from inline components
// It loads config, schema, and updates the component context
if (!componentContext) {
console.error('loadPluginConfig requires component context');
return;
}
console.log('Loading config for plugin:', pluginId);
componentContext.loading = true;
try {
// Load config, schema, and installed plugins (for web_ui_actions) in parallel
let configData, schemaData, pluginsData;
if (window.PluginAPI && window.PluginAPI.batch) {
try {
const results = await window.PluginAPI.batch([
{endpoint: `/plugins/config?plugin_id=${pluginId}`, method: 'GET'},
{endpoint: `/plugins/schema?plugin_id=${pluginId}`, method: 'GET'},
{endpoint: '/plugins/installed', method: 'GET'}
]);
[configData, schemaData, pluginsData] = results;
} catch (batchError) {
console.error('Batch API request failed, falling back to individual requests:', batchError);
// Fall back to individual requests
const [configResponse, schemaResponse, pluginsResponse] = await Promise.all([
fetch(`/api/v3/plugins/config?plugin_id=${pluginId}`).then(r => r.json()).catch(e => ({ status: 'error', message: e.message })),
fetch(`/api/v3/plugins/schema?plugin_id=${pluginId}`).then(r => r.json()).catch(e => ({ status: 'error', message: e.message })),
fetch(`/api/v3/plugins/installed`).then(r => r.json()).catch(e => ({ status: 'error', message: e.message }))
]);
configData = configResponse;
schemaData = schemaResponse;
pluginsData = pluginsResponse;
}
} else {
const [configResponse, schemaResponse, pluginsResponse] = await Promise.all([
fetch(`/api/v3/plugins/config?plugin_id=${pluginId}`).then(r => r.json()).catch(e => ({ status: 'error', message: e.message })),
fetch(`/api/v3/plugins/schema?plugin_id=${pluginId}`).then(r => r.json()).catch(e => ({ status: 'error', message: e.message })),
fetch(`/api/v3/plugins/installed`).then(r => r.json()).catch(e => ({ status: 'error', message: e.message }))
]);
configData = configResponse;
schemaData = schemaResponse;
pluginsData = pluginsResponse;
}
if (configData && configData.status === 'success') {
componentContext.config = configData.data;
} else {
console.warn('Config API returned non-success status:', configData);
// Set defaults if config failed to load
componentContext.config = { enabled: true, display_duration: 30 };
}
if (schemaData && schemaData.status === 'success') {
componentContext.schema = schemaData.data.schema || {};
} else {
console.warn('Schema API returned non-success status:', schemaData);
// Set empty schema as fallback
componentContext.schema = {};
}
if (pluginsData && pluginsData.status === 'success' && pluginsData.data && pluginsData.data.plugins) {
const pluginInfo = pluginsData.data.plugins.find(p => p.id === pluginId);
componentContext.webUiActions = pluginInfo ? (pluginInfo.web_ui_actions || []) : [];
} else {
console.warn('Plugins API returned non-success status:', pluginsData);
componentContext.webUiActions = [];
}
console.log('Loaded config, schema, and actions for', pluginId);
} catch (error) {
console.error('Error loading plugin config:', error);
componentContext.config = { enabled: true, display_duration: 30 };
componentContext.schema = {};
componentContext.webUiActions = [];
} finally {
componentContext.loading = false;
}
},
generateConfigForm: function(pluginId, config, schema, webUiActions, componentContext) {
// Try to get the app component
let appComponent = null;
if (window.Alpine) {
const appElement = document.querySelector('[x-data="app()"]');
if (appElement && appElement._x_dataStack && appElement._x_dataStack[0]) {
appComponent = appElement._x_dataStack[0];
}
}
// If we have access to the app component, use its method
if (appComponent && typeof appComponent.generateConfigForm === 'function') {
return appComponent.generateConfigForm(pluginId, config, schema, webUiActions);
}
// Fallback: return loading message if function not available
if (!pluginId || !config) {
return '<div class="text-gray-500">Loading configuration...</div>';
}
return '<div class="text-gray-500">Configuration form not available yet...</div>';
},
savePluginConfig: async function(pluginId, event, componentContext) {
// Try to get the app component
let appComponent = null;
if (window.Alpine) {
const appElement = document.querySelector('[x-data="app()"]');
if (appElement && appElement._x_dataStack && appElement._x_dataStack[0]) {
appComponent = appElement._x_dataStack[0];
}
}
// If we have access to the app component, use its method
if (appComponent && typeof appComponent.savePluginConfig === 'function') {
return appComponent.savePluginConfig(pluginId, event);
}
console.error('savePluginConfig not available');
throw new Error('Save configuration method not available');
}
};
// ===== Nested Section Toggle =====
window.toggleNestedSection = function(sectionId, event) {