diff --git a/wwwroot/list-component.js b/wwwroot/list-component.js new file mode 100644 index 0000000..4b51f3f --- /dev/null +++ b/wwwroot/list-component.js @@ -0,0 +1,106 @@ +// Simplified List component using urlwidget pattern +// This approach follows the bricks-framework best practices by delegating item rendering to separate .ui files + +bricks.List = class extends bricks.VBox { + constructor(opts) { + super(opts); + this.data_url = opts.data_url || null; + this.items = opts.items || []; + this.item_template_url = opts.item_template_url || null; // URL to the item template .ui file + this.itemHeight = opts.itemHeight || 50; + this._loading = false; + + // Set default dimensions + if (!this.options.width) this.options.width = '100%'; + if (!this.options.height) this.options.height = '100%'; + + // Load data if provided + if (this.data_url) { + this.loadData(); + } else if (this.items.length > 0 && this.item_template_url) { + this.renderItems(); + } + } + + async loadData() { + if (this._loading || !this.data_url) return; + this._loading = true; + + try { + const response = await fetch(this.data_url); + if (response.ok) { + const data = await response.json(); + this.items = Array.isArray(data) ? data : (data.items || []); + this.renderItems(); + } else { + console.error('Failed to load list data:', response.status); + } + } catch (error) { + console.error('Error loading list data:', error); + } finally { + this._loading = false; + } + } + + renderItems() { + // Clear existing children + this.subwidgets = []; + this.children.forEach(child => child.remove()); + this.children = []; + + // Render each item using urlwidget to load the item template + this.items.forEach((item, index) => { + const itemContainer = new bricks.VBox({ + width: '100%', + height: this.itemHeight + 'px' + }); + + // Store item data on the container for reference + itemContainer._listItemData = item; + itemContainer._listItemIndex = index; + + // Create urlwidget binding to load the item template + if (!itemContainer.binds) { + itemContainer.binds = []; + } + + itemContainer.binds.push({ + wid: 'self', + event: 'on_parent', + actiontype: 'urlwidget', + target: 'self', + options: { + url: this.item_template_url, + params: { + item: item, + index: index, + id: 'item_' + index + } + }, + mode: 'replace' + }); + + this.appendChild(itemContainer); + }); + + this.refresh(); + } + + // Public method to reload data + reload() { + if (this.data_url) { + this.loadData(); + } + } + + // Public method to set items directly + setItems(items) { + this.items = items || []; + this.renderItems(); + } +}; + +// Register the List component +bricks.Factory.register('List', bricks.List); + +console.log('List component registered - uses urlwidget pattern for item rendering'); \ No newline at end of file diff --git a/wwwroot/list-item-template.ui b/wwwroot/list-item-template.ui new file mode 100644 index 0000000..c0cb25d --- /dev/null +++ b/wwwroot/list-item-template.ui @@ -0,0 +1,46 @@ +{ + "widgettype": "HBox", + "options": { + "width": "100%", + "height": "50px", + "backgroundColor": "{{ '#22C55E' if item.status == 'Connected' else '#EF4444' }}", + "padding": "8px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "{{item.name}}", + "color": "#FFFFFF", + "fontSize": "16px", + "fontWeight": "bold" + } + }, + { + "widgettype": "Filler" + }, + { + "widgettype": "Button", + "options": { + "label": "Details", + "backgroundColor": "#3B82F6", + "color": "#FFFFFF" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.main-content", + "options": { + "url": "{{entire_url('service-detail.ui')}}", + "params": { + "service_id": "{{item.id}}" + } + }, + "mode": "replace" + } + ] + } + ] +} \ No newline at end of file diff --git a/wwwroot/model-list-item.ui b/wwwroot/model-list-item.ui new file mode 100644 index 0000000..2eb63c1 --- /dev/null +++ b/wwwroot/model-list-item.ui @@ -0,0 +1,93 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "16px", + "border": "1px solid #334155", + "borderRadius": "8px", + "bgcolor": "#1E293B", + "marginBottom": "12px" + }, + "subwidgets": [ + { + "widgettype": "UiStr", + "options": { + "name": "model-name", + "label": "Model Name", + "value": "{{item.name}}", + "width": "100%", + "marginBottom": "8px" + } + }, + { + "widgettype": "UiStr", + "options": { + "name": "provider", + "label": "Provider", + "value": "{{item.provider}}", + "width": "100%", + "marginBottom": "8px" + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "gap": "12px" + }, + "subwidgets": [ + { + "widgettype": "Button", + "options": { + "label": "Update", + "bgcolor": "#3B82F6", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "4px", + "padding": "6px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "registerfunction", + "target": "self", + "rfname": "update_model", + "params": { + "model_id": "{{item.id}}", + "model_name": "{{item.name}}", + "model_provider": "{{item.provider}}" + } + } + ] + }, + { + "widgettype": "Button", + "options": { + "label": "Remove", + "bgcolor": "#EF4444", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "4px", + "padding": "6px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "registerfunction", + "target": "self", + "rfname": "remove_model", + "params": { + "model_id": "{{item.id}}" + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/wwwroot/scripts/list-component.js b/wwwroot/scripts/list-component.js deleted file mode 100644 index acc3126..0000000 --- a/wwwroot/scripts/list-component.js +++ /dev/null @@ -1,149 +0,0 @@ -// Custom List component for bricks-framework -// This component renders a list of items with custom item templates - -bricks.List = class extends bricks.VBox { - constructor(opts) { - super(opts); - this.data_url = opts.data_url || null; - this.items = opts.items || []; - this.itemHeight = opts.itemHeight || 50; - this._loading = false; - - // Set default dimensions if not provided - if (!this.options.width) this.options.width = '100%'; - if (!this.options.height) this.options.height = '100%'; - - // Load data if data_url is provided - if (this.data_url) { - this.loadData(); - } else if (this.items.length > 0) { - this.renderItems(); - } - } - - async loadData() { - if (this._loading || !this.data_url) return; - this._loading = true; - - try { - const response = await fetch(this.data_url); - if (response.ok) { - const data = await response.json(); - this.items = Array.isArray(data) ? data : (data.items || []); - this.renderItems(); - } else { - console.error('Failed to load list data:', response.status); - } - } catch (error) { - console.error('Error loading list data:', error); - } finally { - this._loading = false; - } - } - - renderItems() { - // Clear existing children - this.subwidgets = []; - this.children = []; - - // Render each item using the template defined in subwidgets - this.items.forEach((item, index) => { - const itemWidget = this.createItemWidget(item, index); - if (itemWidget) { - this.appendChild(itemWidget); - } - }); - - // Refresh the display - this.refresh(); - } - - createItemWidget(itemData, index) { - if (!this.template || this.template.length === 0) { - return null; - } - - // Clone the template and process it with item data - const processedTemplate = this.processTemplate(this.template[0], itemData, index); - if (processedTemplate) { - const widget = bricks.Factory.create(processedTemplate); - if (widget) { - // Store reference to item data for event handling - widget._listItemData = itemData; - widget._listItemIndex = index; - return widget; - } - } - return null; - } - - processTemplate(template, itemData, index) { - // This is a simplified template processor - // In a real implementation, you would need proper Jinja2-like processing - const processed = JSON.parse(JSON.stringify(template)); - - // Process options recursively - this.processObject(processed, itemData, index); - - return processed; - } - - processObject(obj, itemData, index) { - if (typeof obj !== 'object' || obj === null) { - return; - } - - for (const key in obj) { - if (typeof obj[key] === 'string') { - // Simple string replacement for {{item.xxx}} patterns - if (obj[key].includes('{{item.')) { - const match = obj[key].match(/{{item\.([^}]+)}}/); - if (match && match[1]) { - const fieldName = match[1]; - obj[key] = obj[key].replace(`{{item.${fieldName}}}`, itemData[fieldName] || ''); - } - } - // Handle conditional expressions like {{ '#22C55E' if item.status == 'Connected' else '#EF4444' }} - if (obj[key].includes('{{ ') && obj[key].includes(' if item.')) { - const fullMatch = obj[key].match(/{{\s*(.*?)\s+if\s+item\.(\w+)\s*==\s*'([^']+)'\s+else\s+(.*?)\s*}}/); - if (fullMatch) { - const trueValue = fullMatch[1].trim().replace(/^['"]|['"]$/g, ''); - const fieldName = fullMatch[2]; - const conditionValue = fullMatch[3]; - const falseValue = fullMatch[4].trim().replace(/^['"]|['"]$/g, ''); - - obj[key] = itemData[fieldName] === conditionValue ? trueValue : falseValue; - } - } - } else if (typeof obj[key] === 'object' && obj[key] !== null) { - this.processObject(obj[key], itemData, index); - } - } - } - - // Public method to reload data - reload() { - if (this.data_url) { - this.loadData(); - } - } - - // Public method to set items directly - setItems(items) { - this.items = items || []; - this.renderItems(); - } - - // Override appendChild to capture the template - appendChild(widget) { - if (!this.template) { - this.template = this.subwidgets || []; - } - super.appendChild(widget); - } -}; - -// Register the List component -bricks.register('List', bricks.List); - -console.log('Custom List component registered with bricks framework'); \ No newline at end of file diff --git a/wwwroot/scripts/settings.js b/wwwroot/scripts/settings.js deleted file mode 100644 index e33ebdf..0000000 --- a/wwwroot/scripts/settings.js +++ /dev/null @@ -1,121 +0,0 @@ -// Register functions for settings management -bricks.registerFunction('save_general_settings', async function(params) { - try { - const formData = new FormData(); - formData.append('default-model', params.default_model); - formData.append('session-timeout', params.session_timeout); - formData.append('auto-save', params.auto_save); - - const response = await fetch('/hermes-web-cli/settings/save/general/', { - method: 'POST', - body: formData - }); - const result = await response.json(); - - if (result.success) { - bricks.showMessage('General settings saved successfully', 'success'); - } else { - bricks.showMessage(result.error || 'Failed to save settings', 'error'); - } - } catch (error) { - bricks.showMessage('Network error: ' + error.message, 'error'); - } -}); - -bricks.registerFunction('save_security_settings', async function(params) { - try { - const formData = new FormData(); - formData.append('require-auth', params.require_auth); - formData.append('encrypt-storage', params.encrypt_storage); - - const response = await fetch('/hermes-web-cli/settings/save/security/', { - method: 'POST', - body: formData - }); - const result = await response.json(); - - if (result.success) { - bricks.showMessage('Security settings saved successfully', 'success'); - } else { - bricks.showMessage(result.error || 'Failed to save settings', 'error'); - } - } catch (error) { - bricks.showMessage('Network error: ' + error.message, 'error'); - } -}); - -bricks.registerFunction('save_appearance_settings', async function(params) { - try { - const formData = new FormData(); - formData.append('theme-select', params.theme); - - const response = await fetch('/hermes-web-cli/settings/save/appearance/', { - method: 'POST', - body: formData - }); - const result = await response.json(); - - if (result.success) { - bricks.showMessage('Appearance settings saved successfully', 'success'); - // Apply theme change - document.body.className = params.theme; - } else { - bricks.showMessage(result.error || 'Failed to save settings', 'error'); - } - } catch (error) { - bricks.showMessage('Network error: ' + error.message, 'error'); - } -}); - -// Register functions for models management -bricks.registerFunction('add_model', async function(params) { - // Navigate to add model page - const mainContent = bricks.getWidget('app.main-content'); - if (mainContent) { - mainContent.loadURL(bricks.entire_url('add_model.ui')); - } -}); - -bricks.registerFunction('update_model', async function(params) { - try { - const formData = new FormData(); - formData.append('model_id', params.model_id); - formData.append('model_name', params.model_name); - formData.append('model_provider', params.model_provider); - - const response = await fetch('/hermes-web-cli/models/update/', { - method: 'POST', - body: formData - }); - const result = await response.json(); - - if (result.success) { - bricks.showMessage('Model updated successfully', 'success'); - // Refresh the models list if needed - } else { - bricks.showMessage(result.error || 'Failed to update model', 'error'); - } - } catch (error) { - bricks.showMessage('Network error: ' + error.message, 'error'); - } -}); - -bricks.registerFunction('remove_model', async function(params) { - try { - const response = await fetch('/hermes-web-cli/models/remove/?id=' + params.model_id); - const result = await response.json(); - - if (result.success) { - bricks.showMessage('Model removed successfully', 'success'); - // Refresh the models list - const modelsList = bricks.getWidget('models-list'); - if (modelsList) { - modelsList.reload(); - } - } else { - bricks.showMessage(result.error || 'Failed to remove model', 'error'); - } - } catch (error) { - bricks.showMessage('Network error: ' + error.message, 'error'); - } -}); \ No newline at end of file diff --git a/wwwroot/service-list-item.ui b/wwwroot/service-list-item.ui new file mode 100644 index 0000000..5e676f7 --- /dev/null +++ b/wwwroot/service-list-item.ui @@ -0,0 +1,139 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "16px", + "border": "1px solid #334155", + "borderRadius": "8px", + "bgcolor": "#1E293B" + }, + "subwidgets": [ + { + "widgettype": "HBox", + "options": { + "width": "100%", + "alignItems": "center", + "marginBottom": "8px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "{{item.name}}", + "fontSize": "18px", + "fontWeight": "600", + "color": "#F8FAFC" + } + }, + { + "widgettype": "Filler" + }, + { + "widgettype": "Badge", + "options": { + "text": "{{item.status}}", + "bgcolor": "{{ '#22C55E' if item.status == 'Connected' else '#EF4444' }}", + "color": "#FFFFFF", + "borderRadius": "12px", + "padding": "4px 12px", + "fontSize": "12px" + } + } + ] + }, + { + "widgettype": "Text", + "options": { + "text": "{{item.endpoint}}", + "fontSize": "14px", + "color": "#CBD5E1", + "marginBottom": "8px" + } + }, + { + "widgettype": "HBox", + "options": { + "width": "100%", + "gap": "12px" + }, + "subwidgets": [ + { + "widgettype": "Button", + "options": { + "label": "Edit", + "bgcolor": "#3B82F6", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "4px", + "padding": "6px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.main-content", + "options": { + "url": "{{entire_url('edit_service.ui')}}", + "params": { + "service_id": "{{item.id}}" + } + }, + "mode": "replace" + } + ] + }, + { + "widgettype": "Button", + "options": { + "label": "Remove", + "bgcolor": "#EF4444", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "4px", + "padding": "6px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "registerfunction", + "target": "self", + "rfname": "remove_service", + "params": { + "service_id": "{{item.id}}" + } + } + ] + }, + { + "widgettype": "Button", + "options": { + "label": "Test Connection", + "bgcolor": "#8B5CF6", + "color": "#FFFFFF", + "border": "none", + "borderRadius": "4px", + "padding": "6px 12px", + "fontSize": "12px" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "registerfunction", + "target": "self", + "rfname": "test_service_connection", + "params": { + "service_id": "{{item.id}}" + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/wwwroot/scripts/services.js b/wwwroot/services.js similarity index 89% rename from wwwroot/scripts/services.js rename to wwwroot/services.js index e034aaa..08eca45 100644 --- a/wwwroot/scripts/services.js +++ b/wwwroot/services.js @@ -1,5 +1,5 @@ // Register functions for services management -bricks.registerFunction('remove_service', async function(params) { +bricks.RF.register('remove_service', async function(params) { try { const response = await fetch('/hermes-web-cli/services/remove/?id=' + params.service_id); const result = await response.json(); @@ -19,7 +19,7 @@ bricks.registerFunction('remove_service', async function(params) { } }); -bricks.registerFunction('test_service_connection', async function(params) { +bricks.RF.register('test_service_connection', async function(params) { try { const response = await fetch('/hermes-web-cli/services/test/?id=' + params.service_id); const result = await response.json(); diff --git a/wwwroot/services.ui b/wwwroot/services.ui index 73ca2a7..0027354 100644 --- a/wwwroot/services.ui +++ b/wwwroot/services.ui @@ -70,146 +70,9 @@ "width": "100%", "height": "calc(100% - 100px)", "itemHeight": "100px", - "data_url": "/hermes-web-cli/services/list/" - }, - "subwidgets": [ - { - "widgettype": "VBox", - "options": { - "width": "100%", - "height": "100%", - "padding": "16px", - "border": "1px solid #334155", - "borderRadius": "8px", - "bgcolor": "#1E293B" - }, - "subwidgets": [ - { - "widgettype": "HBox", - "options": { - "width": "100%", - "alignItems": "center", - "marginBottom": "8px" - }, - "subwidgets": [ - { - "widgettype": "Text", - "options": { - "text": "{{item.name}}", - "fontSize": "18px", - "fontWeight": "600", - "color": "#F8FAFC" - } - }, - { - "widgettype": "Filler" - }, - { - "widgettype": "Badge", - "options": { - "text": "{{item.status}}", - "bgcolor": "{{ '#22C55E' if item.status == 'Connected' else '#EF4444' }}", - "color": "#FFFFFF", - "borderRadius": "12px", - "padding": "4px 12px", - "fontSize": "12px" - } - } - ] - }, - { - "widgettype": "Text", - "options": { - "text": "{{item.endpoint}}", - "fontSize": "14px", - "color": "#CBD5E1", - "marginBottom": "8px" - } - }, - { - "widgettype": "HBox", - "options": { - "width": "100%", - "gap": "12px" - }, - "subwidgets": [ - { - "widgettype": "Button", - "options": { - "label": "Edit", - "bgcolor": "#3B82F6", - "color": "#FFFFFF", - "border": "none", - "borderRadius": "4px", - "padding": "6px 12px", - "fontSize": "12px" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "urlwidget", - "target": "app.main-content", - "options": { - "url": "{{entire_url('edit_service.ui?id={{item.id}}')}}" - }, - "mode": "replace" - } - ] - }, - { - "widgettype": "Button", - "options": { - "label": "Remove", - "bgcolor": "#EF4444", - "color": "#FFFFFF", - "border": "none", - "borderRadius": "4px", - "padding": "6px 12px", - "fontSize": "12px" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "registerfunction", - "target": "self", - "rfname": "remove_service", - "params": { - "service_id": "{{item.id}}" - } - } - ] - }, - { - "widgettype": "Button", - "options": { - "label": "Test Connection", - "bgcolor": "#8B5CF6", - "color": "#FFFFFF", - "border": "none", - "borderRadius": "4px", - "padding": "6px 12px", - "fontSize": "12px" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "registerfunction", - "target": "self", - "rfname": "test_service_connection", - "params": { - "service_id": "{{item.id}}" - } - } - ] - } - ] - } - ] - } - ] + "data_url": "/hermes-web-cli/services/list/", + "item_template_url": "{{entire_url('service-list-item.ui')}}" + } } ] } \ No newline at end of file diff --git a/wwwroot/session-list-item.ui b/wwwroot/session-list-item.ui new file mode 100644 index 0000000..dcc49e0 --- /dev/null +++ b/wwwroot/session-list-item.ui @@ -0,0 +1,67 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "12px", + "border": "1px solid #334155", + "borderRadius": "8px", + "bgcolor": "#1E293B", + "cursor": "pointer" + }, + "subwidgets": [ + { + "widgettype": "HBox", + "options": { + "width": "100%", + "alignItems": "center" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "text": "{{item.name}}", + "fontSize": "16px", + "fontWeight": "600", + "color": "#F8FAFC" + } + }, + { + "widgettype": "Filler" + }, + { + "widgettype": "Text", + "options": { + "text": "{{item.last_active}}", + "fontSize": "12px", + "color": "#94A3B8" + } + } + ] + }, + { + "widgettype": "Text", + "options": { + "text": "{{item.model}}", + "fontSize": "14px", + "color": "#CBD5E1", + "marginTop": "4px" + } + } + ], + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.main-content", + "options": { + "url": "{{entire_url('session_detail.ui')}}", + "params": { + "session_id": "{{item.id}}" + } + }, + "mode": "replace" + } + ] +} \ No newline at end of file diff --git a/wwwroot/sessions.ui b/wwwroot/sessions.ui index 3cc456b..e3ce6aa 100644 --- a/wwwroot/sessions.ui +++ b/wwwroot/sessions.ui @@ -70,74 +70,9 @@ "width": "100%", "height": "calc(100% - 100px)", "itemHeight": "80px", - "data_url": "/hermes-web-cli/sessions/list/" - }, - "subwidgets": [ - { - "widgettype": "VBox", - "options": { - "width": "100%", - "height": "100%", - "padding": "12px", - "border": "1px solid #334155", - "borderRadius": "8px", - "bgcolor": "#1E293B", - "cursor": "pointer" - }, - "subwidgets": [ - { - "widgettype": "HBox", - "options": { - "width": "100%", - "alignItems": "center" - }, - "subwidgets": [ - { - "widgettype": "Text", - "options": { - "text": "{{item.name}}", - "fontSize": "16px", - "fontWeight": "600", - "color": "#F8FAFC" - } - }, - { - "widgettype": "Filler" - }, - { - "widgettype": "Text", - "options": { - "text": "{{item.last_active}}", - "fontSize": "12px", - "color": "#94A3B8" - } - } - ] - }, - { - "widgettype": "Text", - "options": { - "text": "{{item.model}}", - "fontSize": "14px", - "color": "#CBD5E1", - "marginTop": "4px" - } - } - ], - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "urlwidget", - "target": "app.main-content", - "options": { - "url": "{{entire_url('session_detail.ui?id={{item.id}}')}}" - }, - "mode": "replace" - } - ] - } - ] + "data_url": "/hermes-web-cli/sessions/list/", + "item_template_url": "{{entire_url('session-list-item.ui')}}" + } } ] } \ No newline at end of file diff --git a/wwwroot/settings.js b/wwwroot/settings.js new file mode 100644 index 0000000..5242ff3 --- /dev/null +++ b/wwwroot/settings.js @@ -0,0 +1,121 @@ + 1|// Register functions for settings management + 2|bricks.RF.register('save_general_settings', async function(params) { + 3| try { + 4| const formData = new FormData(); + 5| formData.append('default-model', params.default_model); + 6| formData.append('session-timeout', params.session_timeout); + 7| formData.append('auto-save', params.auto_save); + 8| + 9| const response = await fetch('/hermes-web-cli/settings/save/general/', { + 10| method: 'POST', + 11| body: formData + 12| }); + 13| const result = await response.json(); + 14| + 15| if (result.success) { + 16| bricks.showMessage('General settings saved successfully', 'success'); + 17| } else { + 18| bricks.showMessage(result.error || 'Failed to save settings', 'error'); + 19| } + 20| } catch (error) { + 21| bricks.showMessage('Network error: ' + error.message, 'error'); + 22| } + 23|}); + 24| + 25|bricks.RF.register('save_security_settings', async function(params) { + 26| try { + 27| const formData = new FormData(); + 28| formData.append('require-auth', params.require_auth); + 29| formData.append('encrypt-storage', params.encrypt_storage); + 30| + 31| const response = await fetch('/hermes-web-cli/settings/save/security/', { + 32| method: 'POST', + 33| body: formData + 34| }); + 35| const result = await response.json(); + 36| + 37| if (result.success) { + 38| bricks.showMessage('Security settings saved successfully', 'success'); + 39| } else { + 40| bricks.showMessage(result.error || 'Failed to save settings', 'error'); + 41| } + 42| } catch (error) { + 43| bricks.showMessage('Network error: ' + error.message, 'error'); + 44| } + 45|}); + 46| + 47|bricks.RF.register('save_appearance_settings', async function(params) { + 48| try { + 49| const formData = new FormData(); + 50| formData.append('theme-select', params.theme); + 51| + 52| const response = await fetch('/hermes-web-cli/settings/save/appearance/', { + 53| method: 'POST', + 54| body: formData + 55| }); + 56| const result = await response.json(); + 57| + 58| if (result.success) { + 59| bricks.showMessage('Appearance settings saved successfully', 'success'); + 60| // Apply theme change + 61| document.body.className = params.theme; + 62| } else { + 63| bricks.showMessage(result.error || 'Failed to save settings', 'error'); + 64| } + 65| } catch (error) { + 66| bricks.showMessage('Network error: ' + error.message, 'error'); + 67| } + 68|}); + 69| + 70|// Register functions for models management + 71|bricks.RF.register('add_model', async function(params) { + 72| // Navigate to add model page + 73| const mainContent = bricks.getWidget('app.main-content'); + 74| if (mainContent) { + 75| mainContent.loadURL(bricks.entire_url('add_model.ui')); + 76| } + 77|}); + 78| + 79|bricks.RF.register('update_model', async function(params) { + 80| try { + 81| const formData = new FormData(); + 82| formData.append('model_id', params.model_id); + 83| formData.append('model_name', params.model_name); + 84| formData.append('model_provider', params.model_provider); + 85| + 86| const response = await fetch('/hermes-web-cli/models/update/', { + 87| method: 'POST', + 88| body: formData + 89| }); + 90| const result = await response.json(); + 91| + 92| if (result.success) { + 93| bricks.showMessage('Model updated successfully', 'success'); + 94| // Refresh the models list if needed + 95| } else { + 96| bricks.showMessage(result.error || 'Failed to update model', 'error'); + 97| } + 98| } catch (error) { + 99| bricks.showMessage('Network error: ' + error.message, 'error'); + 100| } + 101|}); + 102| + 103|bricks.RF.register('remove_model', async function(params) { + 104| try { + 105| const response = await fetch('/hermes-web-cli/models/remove/?id=' + params.model_id); + 106| const result = await response.json(); + 107| + 108| if (result.success) { + 109| bricks.showMessage('Model removed successfully', 'success'); + 110| // Refresh the models list + 111| const modelsList = bricks.getWidget('models-list'); + 112| if (modelsList) { + 113| modelsList.reload(); + 114| } + 115| } else { + 116| bricks.showMessage(result.error || 'Failed to remove model', 'error'); + 117| } + 118| } catch (error) { + 119| bricks.showMessage('Network error: ' + error.message, 'error'); + 120| } + 121|}); \ No newline at end of file diff --git a/wwwroot/settings.ui b/wwwroot/settings.ui index c61f24c..c61bb5f 100644 --- a/wwwroot/settings.ui +++ b/wwwroot/settings.ui @@ -146,103 +146,9 @@ "width": "100%", "height": "calc(100% - 80px)", "itemHeight": "120px", - "items": [] - }, - "subwidgets": [ - { - "widgettype": "VBox", - "options": { - "width": "100%", - "height": "100%", - "padding": "16px", - "border": "1px solid #334155", - "borderRadius": "8px", - "bgcolor": "#1E293B", - "marginBottom": "12px" - }, - "subwidgets": [ - { - "widgettype": "UiStr", - "options": { - "name": "model-name", - "label": "Model Name", - "value": "{{data.name}}", - "width": "100%", - "marginBottom": "8px" - } - }, - { - "widgettype": "UiStr", - "options": { - "name": "provider", - "label": "Provider", - "value": "{{data.provider}}", - "width": "100%", - "marginBottom": "8px" - } - }, - { - "widgettype": "HBox", - "options": { - "width": "100%", - "gap": "12px" - }, - "subwidgets": [ - { - "widgettype": "Button", - "options": { - "label": "Update", - "bgcolor": "#3B82F6", - "color": "#FFFFFF", - "border": "none", - "borderRadius": "4px", - "padding": "6px 12px", - "fontSize": "12px" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "registerfunction", - "target": "self", - "rfname": "update_model", - "params": { - "model_id": "{{data.id}}", - "model_name": "{{data.name}}", - "model_provider": "{{data.provider}}" - } - } - ] - }, - { - "widgettype": "Button", - "options": { - "label": "Remove", - "bgcolor": "#EF4444", - "color": "#FFFFFF", - "border": "none", - "borderRadius": "4px", - "padding": "6px 12px", - "fontSize": "12px" - }, - "binds": [ - { - "wid": "self", - "event": "click", - "actiontype": "registerfunction", - "target": "self", - "rfname": "remove_model", - "params": { - "model_id": "{{data.id}}" - } - } - ] - } - ] - } - ] - } - ] + "data_url": "/hermes-web-cli/models/list/", + "item_template_url": "{{entire_url('model-list-item.ui')}}" + } }, { "widgettype": "Button",