chore: Rename Astroglobe -> Astrolabe

This commit is contained in:
Chris Coutinho
2025-12-18 00:02:08 +01:00
parent 24898439cb
commit d235dfa023
80 changed files with 256 additions and 362 deletions
+11
View File
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
use OCP\Util;
Util::addScript(OCA\Astrolabe\AppInfo\Application::APP_ID, OCA\Astrolabe\AppInfo\Application::APP_ID . '-main');
?>
<div id="astrolabe"></div>
+397
View File
@@ -0,0 +1,397 @@
<?php
/**
* Admin settings template for Astrolabe.
*
* Displays semantic search service status, indexing metrics, configuration,
* and provides administrative controls.
*
* @var array $_ Template parameters
* @var array $_['serverStatus'] Server status from API
* @var array $_['vectorSyncStatus'] Vector sync metrics from API
* @var string $_['serverUrl'] Configured Astrolabe service URL
* @var bool $_['apiKeyConfigured'] Whether API key is set in config.php
* @var bool $_['vectorSyncEnabled'] Whether vector sync is enabled
*/
script('astrolabe', 'astrolabe-adminSettings');
style('astrolabe', 'astrolabe-settings');
?>
<div id="mcp-admin-settings" class="section">
<h2><?php p($l->t('Astrolabe Administration')); ?></h2>
<div class="mcp-settings-info">
<p><?php p($l->t('Monitor and configure the semantic search service for your Nextcloud instance.')); ?></p>
</div>
<!-- Configuration Status -->
<div class="mcp-status-card">
<h3><?php p($l->t('Configuration')); ?></h3>
<table class="mcp-info-table">
<tr>
<td><strong><?php p($l->t('Service URL')); ?></strong></td>
<td>
<?php if (!empty($_['serverUrl'])): ?>
<code><?php p($_['serverUrl']); ?></code>
<?php else: ?>
<span class="error"><?php p($l->t('Not configured')); ?></span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php p($l->t('API Key')); ?></strong></td>
<td>
<?php if ($_['apiKeyConfigured']): ?>
<span class="badge badge-success">
<span class="icon icon-checkmark-white"></span>
<?php p($l->t('Configured')); ?>
</span>
<?php else: ?>
<span class="badge badge-warning">
<span class="icon icon-alert"></span>
<?php p($l->t('Not configured')); ?>
</span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php p($l->t('OAuth Client ID')); ?></strong></td>
<td>
<?php if ($_['clientIdConfigured']): ?>
<span class="badge badge-success">
<span class="icon icon-checkmark-white"></span>
<?php p($l->t('Configured')); ?>
</span>
<?php else: ?>
<span class="badge badge-warning">
<span class="icon icon-alert"></span>
<?php p($l->t('Not configured - OAuth will not work')); ?>
</span>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php p($l->t('OAuth Client Secret')); ?></strong></td>
<td>
<?php if ($_['clientSecretConfigured']): ?>
<span class="badge badge-success">
<span class="icon icon-checkmark-white"></span>
<?php p($l->t('Configured')); ?>
</span>
<?php else: ?>
<span class="badge badge-info">
<?php p($l->t('Optional - Uses PKCE fallback')); ?>
</span>
<?php endif; ?>
</td>
</tr>
</table>
<?php if (empty($_['serverUrl']) || !$_['apiKeyConfigured'] || !$_['clientIdConfigured']): ?>
<div class="notecard notecard-warning">
<p><strong><?php p($l->t('Configuration Required')); ?></strong></p>
<p><?php p($l->t('Add the following to your config.php:')); ?></p>
<pre><code>'mcp_server_url' => 'http://localhost:8000',
'mcp_server_api_key' => 'your-secret-api-key',
'astrolabe_client_id' => 'your-oauth-client-id',</code></pre>
<p class="mcp-help-text">
<a href="https://github.com/cbcoutinho/nextcloud-mcp-server" target="_blank">
<?php p($l->t('See documentation for details')); ?>
</a>
</p>
</div>
<?php endif; ?>
<?php if (!$_['clientSecretConfigured']): ?>
<div class="notecard notecard-info">
<p><strong><?php p($l->t('Optional: Confidential OAuth Client')); ?></strong></p>
<p><?php p($l->t('To use refresh tokens for long-lived sessions, generate a client secret:')); ?></p>
<pre><code>openssl rand -hex 32</code></pre>
<p><?php p($l->t('Then add it to your config.php:')); ?></p>
<pre><code>'astrolabe_client_secret' => 'your-generated-secret',</code></pre>
<p class="mcp-help-text">
<?php p($l->t('Without a client secret, the system will use PKCE (public client) authentication. Both methods work, but confidential clients provide better security for long-lived sessions.')); ?>
</p>
</div>
<?php endif; ?>
</div>
<!-- Service Status -->
<div class="mcp-status-card">
<h3><?php p($l->t('Service Status')); ?></h3>
<table class="mcp-info-table">
<tr>
<td><strong><?php p($l->t('Version')); ?></strong></td>
<td><?php p($_['serverStatus']['version'] ?? 'Unknown'); ?></td>
</tr>
<tr>
<td><strong><?php p($l->t('Uptime')); ?></strong></td>
<td>
<?php if (isset($_['serverStatus']['uptime_seconds'])): ?>
<?php
$uptime = $_['serverStatus']['uptime_seconds'];
$hours = floor($uptime / 3600);
$minutes = floor(($uptime % 3600) / 60);
p(sprintf('%d hours, %d minutes', $hours, $minutes));
?>
<?php else: ?>
<?php p($l->t('Unknown')); ?>
<?php endif; ?>
</td>
</tr>
<tr>
<td><strong><?php p($l->t('Semantic Search')); ?></strong></td>
<td>
<?php if ($_['vectorSyncEnabled']): ?>
<span class="badge badge-success">
<span class="icon icon-checkmark-white"></span>
<?php p($l->t('Enabled')); ?>
</span>
<?php else: ?>
<span class="badge badge-neutral">
<?php p($l->t('Disabled')); ?>
</span>
<?php endif; ?>
</td>
</tr>
</table>
</div>
<!-- Indexing Metrics -->
<?php if ($_['vectorSyncEnabled'] && !isset($_['vectorSyncStatus']['error'])): ?>
<div class="mcp-status-card" id="vector-sync-metrics">
<h3><?php p($l->t('Indexing Metrics')); ?></h3>
<table class="mcp-info-table">
<tr>
<td><strong><?php p($l->t('Status')); ?></strong></td>
<td>
<?php
$status = $_['vectorSyncStatus']['status'] ?? 'unknown';
$statusClass = $status === 'idle' ? 'success' : ($status === 'syncing' ? 'info' : 'neutral');
?>
<span class="badge badge-<?php p($statusClass); ?>">
<?php p(ucfirst($status)); ?>
</span>
</td>
</tr>
<tr>
<td><strong><?php p($l->t('Indexed Documents')); ?></strong></td>
<td><?php p(number_format($_['vectorSyncStatus']['indexed_documents'] ?? 0)); ?></td>
</tr>
<tr>
<td><strong><?php p($l->t('Pending Documents')); ?></strong></td>
<td><?php p(number_format($_['vectorSyncStatus']['pending_documents'] ?? 0)); ?></td>
</tr>
<tr>
<td><strong><?php p($l->t('Last Sync')); ?></strong></td>
<td><?php p($_['vectorSyncStatus']['last_sync_time'] ?? 'Never'); ?></td>
</tr>
<tr>
<td><strong><?php p($l->t('Processing Rate')); ?></strong></td>
<td><?php p(sprintf('%.1f docs/sec', $_['vectorSyncStatus']['documents_per_second'] ?? 0)); ?></td>
</tr>
<tr>
<td><strong><?php p($l->t('Errors (24h)')); ?></strong></td>
<td>
<?php
$errors = $_['vectorSyncStatus']['errors_24h'] ?? 0;
if ($errors > 0): ?>
<span class="error"><?php p($errors); ?></span>
<?php else: ?>
<?php p('0'); ?>
<?php endif; ?>
</td>
</tr>
</table>
<p class="mcp-help-text">
<?php p($l->t('Metrics are updated in real-time. Refresh the page to see latest values.')); ?>
</p>
</div>
<?php elseif ($_['vectorSyncEnabled']): ?>
<div class="mcp-status-card mcp-error">
<h3><?php p($l->t('Indexing Metrics')); ?></h3>
<div class="notecard notecard-error">
<p><?php p($l->t('Failed to retrieve indexing status:')); ?></p>
<p><code><?php p($_['vectorSyncStatus']['error'] ?? 'Unknown error'); ?></code></p>
</div>
</div>
<?php endif; ?>
<!-- Search Settings -->
<?php if ($_['vectorSyncEnabled']): ?>
<div class="mcp-status-card" id="search-settings">
<h3><?php p($l->t('AI Search Provider Settings')); ?></h3>
<p class="mcp-settings-description">
<?php p($l->t('Configure the default search parameters for the AI Search provider in Nextcloud unified search.')); ?>
</p>
<form id="astrolabe-search-settings-form" class="mcp-settings-form">
<div class="mcp-form-group">
<label for="search-algorithm"><?php p($l->t('Search Algorithm')); ?></label>
<select id="search-algorithm" name="algorithm" class="mcp-select">
<option value="hybrid" <?php if ($_['searchSettings']['algorithm'] === 'hybrid') {
echo 'selected';
} ?>>
<?php p($l->t('Hybrid (Recommended)')); ?>
</option>
<option value="semantic" <?php if ($_['searchSettings']['algorithm'] === 'semantic') {
echo 'selected';
} ?>>
<?php p($l->t('Semantic Only')); ?>
</option>
<option value="bm25" <?php if ($_['searchSettings']['algorithm'] === 'bm25') {
echo 'selected';
} ?>>
<?php p($l->t('Keyword (BM25) Only')); ?>
</option>
</select>
<p class="mcp-help-text">
<?php p($l->t('Hybrid combines semantic understanding with keyword matching. Semantic finds conceptually similar content. BM25 matches exact keywords.')); ?>
</p>
</div>
<div class="mcp-form-group">
<label for="search-fusion"><?php p($l->t('Fusion Method')); ?></label>
<select id="search-fusion" name="fusion" class="mcp-select">
<option value="rrf" <?php if ($_['searchSettings']['fusion'] === 'rrf') {
echo 'selected';
} ?>>
<?php p($l->t('RRF - Reciprocal Rank Fusion (Recommended)')); ?>
</option>
<option value="dbsf" <?php if ($_['searchSettings']['fusion'] === 'dbsf') {
echo 'selected';
} ?>>
<?php p($l->t('DBSF - Distribution-Based Score Fusion')); ?>
</option>
</select>
<p class="mcp-help-text">
<?php p($l->t('Only applies to hybrid search. RRF balances results well for most queries. DBSF may work better when keyword matches are over/under-weighted.')); ?>
</p>
</div>
<div class="mcp-form-group">
<label for="search-score-threshold">
<?php p($l->t('Minimum Score Threshold')); ?>:
<span id="score-threshold-value"><?php p($_['searchSettings']['scoreThreshold']); ?>%</span>
</label>
<input type="range"
id="search-score-threshold"
name="scoreThreshold"
min="0"
max="100"
step="5"
value="<?php p($_['searchSettings']['scoreThreshold']); ?>"
class="mcp-range" />
<p class="mcp-help-text">
<?php p($l->t('Filter out results below this relevance score. Set to 0 to show all results.')); ?>
</p>
</div>
<div class="mcp-form-group">
<label for="search-limit"><?php p($l->t('Maximum Results')); ?></label>
<input type="number"
id="search-limit"
name="limit"
min="5"
max="100"
step="5"
value="<?php p($_['searchSettings']['limit']); ?>"
class="mcp-input" />
<p class="mcp-help-text">
<?php p($l->t('Maximum number of results to return per search query (5-100).')); ?>
</p>
</div>
<div class="mcp-form-actions">
<button type="submit" class="primary">
<?php p($l->t('Save Settings')); ?>
</button>
<span id="search-settings-status" class="mcp-status-message"></span>
</div>
</form>
</div>
<?php endif; ?>
<!-- Webhook Management -->
<?php if ($_['vectorSyncEnabled']): ?>
<div class="mcp-status-card" id="webhook-presets">
<h3><?php p($l->t('Webhook Management')); ?></h3>
<p class="mcp-settings-description">
<?php p($l->t('Configure real-time synchronization for Nextcloud apps using webhooks. Webhooks provide instant updates to the MCP server when content changes.')); ?>
</p>
<div id="webhook-presets-container">
<div class="mcp-loading">
<?php p($l->t('Loading webhook presets...')); ?>
</div>
</div>
<div class="notecard notecard-info">
<p><strong><?php p($l->t('How Webhooks Work')); ?></strong></p>
<ul>
<li><?php p($l->t('Enable a preset to register webhooks for that app with the MCP server')); ?></li>
<li><?php p($l->t('When content changes in Nextcloud, webhooks notify the MCP server instantly')); ?></li>
<li><?php p($l->t('The MCP server updates its vector index in real-time for semantic search')); ?></li>
<li><?php p($l->t('Disable a preset to stop receiving updates for that app')); ?></li>
</ul>
</div>
<div class="notecard notecard-warning">
<p><strong><?php p($l->t('Requirements')); ?></strong></p>
<ul>
<li><?php p($l->t('The webhook_listeners app must be installed and enabled in Nextcloud')); ?></li>
<li><?php p($l->t('The MCP server must be reachable from your Nextcloud instance')); ?></li>
<li><?php p($l->t('You must have authorized Astrolabe with the MCP server (see Personal Settings)')); ?></li>
</ul>
</div>
</div>
<?php endif; ?>
<!-- Capabilities -->
<div class="mcp-status-card">
<h3><?php p($l->t('Capabilities')); ?></h3>
<ul class="mcp-feature-list">
<li>
<span class="icon icon-search"></span>
<strong><?php p($l->t('Semantic Search')); ?></strong>
<p><?php p($l->t('Search by meaning across Notes, Files, Calendar, and Deck using natural language queries.')); ?></p>
</li>
<?php if ($_['vectorSyncEnabled']): ?>
<li>
<span class="icon icon-category-monitoring"></span>
<strong><?php p($l->t('Vector Visualization')); ?></strong>
<p><?php p($l->t('Explore content relationships in an interactive 2D visualization.')); ?></p>
</li>
<?php endif; ?>
<li>
<span class="icon icon-user"></span>
<strong><?php p($l->t('Per-User Indexing')); ?></strong>
<p><?php p($l->t('Users control their own content indexing via Personal Settings.')); ?></p>
</li>
<li>
<span class="icon icon-toggle"></span>
<strong><?php p($l->t('Hybrid Search')); ?></strong>
<p><?php p($l->t('Combines semantic understanding with keyword matching for optimal results.')); ?></p>
</li>
</ul>
</div>
<!-- Documentation -->
<div class="mcp-status-card">
<h3><?php p($l->t('Documentation')); ?></h3>
<ul class="mcp-links">
<li>
<a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/configuration.md" target="_blank">
<?php p($l->t('Configuration Guide')); ?>
</a>
</li>
<li>
<a href="https://github.com/cbcoutinho/nextcloud-mcp-server" target="_blank">
<?php p($l->t('GitHub Repository')); ?>
</a>
</li>
</ul>
</div>
</div>
+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* Error template for MCP Server UI settings.
*
* Displayed when the MCP server cannot be reached or returns an error.
*
* @var array $_ Template parameters
* @var string $_['error'] Error title
* @var string $_['details'] Error details/message
* @var string $_['server_url'] Configured server URL (optional)
* @var string $_['help_text'] Additional help text (optional)
*/
style('astrolabe', 'astrolabe-settings');
?>
<div class="mcp-settings-error">
<div class="notecard notecard-error">
<h3>
<span class="icon icon-error"></span>
<?php p($_['error'] ?? 'Error'); ?>
</h3>
<?php if (isset($_['details'])): ?>
<p><strong><?php p($l->t('Details:')); ?></strong></p>
<p><code><?php p($_['details']); ?></code></p>
<?php endif; ?>
<?php if (isset($_['server_url'])): ?>
<p><strong><?php p($l->t('Server URL:')); ?></strong></p>
<p><code><?php p($_['server_url']); ?></code></p>
<?php endif; ?>
<?php if (isset($_['help_text'])): ?>
<p class="mcp-help-text"><?php p($_['help_text']); ?></p>
<?php endif; ?>
<h4><?php p($l->t('Troubleshooting Steps:')); ?></h4>
<ol>
<li><?php p($l->t('Verify the MCP server is running and accessible')); ?></li>
<li><?php p($l->t('Check that mcp_server_url in config.php is correct')); ?></li>
<li><?php p($l->t('Ensure mcp_server_api_key matches the server configuration')); ?></li>
<li><?php p($l->t('Check firewall rules and network connectivity')); ?></li>
<li><?php p($l->t('Review MCP server logs for errors')); ?></li>
</ol>
<p>
<a href="https://github.com/cbcoutinho/nextcloud-mcp-server/blob/master/docs/ADR-018-nextcloud-php-app-for-settings-ui.md" target="_blank" class="button">
<?php p($l->t('View Documentation')); ?>
</a>
</p>
</div>
</div>
@@ -0,0 +1,127 @@
<?php
/**
* OAuth authorization required template.
*
* Shown when user needs to authorize Astrolabe for semantic search.
* Implements OAuth 2.0 Authorization Code flow with PKCE.
*
* @var array $_ Template parameters
* @var string $_['oauth_url'] URL to initiate OAuth flow
* @var string $_['server_url'] Astrolabe service URL
* @var bool $_['has_expired'] Whether token exists but is expired
* @var string|null $_['error_message'] Optional error message to display
*/
use OCP\Util;
Util::addStyle('astrolabe', 'astrolabe-settings');
?>
<div id="mcp-personal-settings">
<div class="mcp-settings-info">
<p><?php p($l->t('AI-powered semantic search across your Nextcloud content.')); ?></p>
</div>
<?php if (isset($_['error_message'])): ?>
<div class="mcp-status-card mcp-error">
<h3>
<span class="icon icon-error"></span>
<?php p($l->t('Session Expired')); ?>
</h3>
<p><?php p($_['error_message']); ?></p>
</div>
<?php endif; ?>
<div class="mcp-status-card">
<h3>
<span class="icon icon-search"></span>
<?php p($l->t('Enable Semantic Search')); ?>
</h3>
<?php if (isset($_['has_expired']) && $_['has_expired']): ?>
<p>
<?php p($l->t('Your authorization has expired. Please sign in again to continue using semantic search.')); ?>
</p>
<?php else: ?>
<p>
<?php p($l->t('To search your content by meaning, Astrolabe needs permission to index your Nextcloud data.')); ?>
</p>
<?php endif; ?>
<p>
<strong><?php p($l->t('What happens next?')); ?></strong>
</p>
<ol class="mcp-help-text">
<li><?php p($l->t('Sign in to confirm your identity')); ?></li>
<li><?php p($l->t('Grant permission to index your content')); ?></li>
<li><?php p($l->t('Your content will be indexed for semantic search')); ?></li>
<li><?php p($l->t('Start searching with natural language')); ?></li>
</ol>
<h4><?php p($l->t('Content to be Indexed')); ?></h4>
<ul class="mcp-feature-list">
<li>
<span class="icon icon-files"></span>
<div>
<strong><?php p($l->t('Notes & Files')); ?></strong>
<p><?php p($l->t('Your notes and documents will be searchable by meaning')); ?></p>
</div>
</li>
<li>
<span class="icon icon-calendar"></span>
<div>
<strong><?php p($l->t('Calendar & Tasks')); ?></strong>
<p><?php p($l->t('Find events and tasks with natural language queries')); ?></p>
</div>
</li>
<li>
<span class="icon icon-category-dashboard"></span>
<div>
<strong><?php p($l->t('Deck Cards')); ?></strong>
<p><?php p($l->t('Search across your Deck boards and cards')); ?></p>
</div>
</li>
</ul>
<div style="margin-top: 20px;">
<a href="<?php p($_['oauth_url']); ?>" class="button primary">
<span class="icon icon-play"></span>
<?php if (isset($_['has_expired']) && $_['has_expired']): ?>
<?php p($l->t('Sign In Again')); ?>
<?php else: ?>
<?php p($l->t('Enable Semantic Search')); ?>
<?php endif; ?>
</a>
</div>
<p class="mcp-help-text" style="margin-top: 16px;">
<?php p($l->t('You can disable indexing at any time from this settings page.')); ?>
</p>
</div>
<div class="mcp-status-card">
<h3>
<span class="icon icon-info"></span>
<?php p($l->t('About Astrolabe')); ?>
</h3>
<p>
<?php p($l->t('Astrolabe enables semantic search - finding content by meaning rather than exact keywords. Ask questions like "meeting notes from last week" or "recipes with chicken" to find relevant documents.')); ?>
</p>
<p class="mcp-help-text">
<?php p($l->t('Your content is processed to understand its meaning, enabling powerful natural language search across all your Nextcloud data.')); ?>
</p>
<ul class="mcp-links">
<li>
<a href="https://github.com/cbcoutinho/nextcloud-mcp-server" target="_blank" rel="noopener noreferrer">
<span class="icon icon-external"></span>
<?php p($l->t('Learn More')); ?>
</a>
</li>
</ul>
</div>
</div>
+190
View File
@@ -0,0 +1,190 @@
<?php
/**
* Personal settings template for Astrolabe.
*
* Displays semantic search status, background indexing access,
* and provides controls for managing content indexing.
*
* @var array $_ Template parameters
* @var string $_['userId'] Current user ID
* @var array $_['serverStatus'] Server status from API
* @var array $_['session'] User session details from API
* @var bool $_['vectorSyncEnabled'] Whether vector sync is enabled
* @var bool $_['backgroundAccessGranted'] Whether user has granted background access
* @var string $_['serverUrl'] Astrolabe service URL
*/
// Get URL generator from Nextcloud's service container
$urlGenerator = \OC::$server->getURLGenerator();
script('astrolabe', 'astrolabe-personalSettings');
style('astrolabe', 'astrolabe-settings');
?>
<div id="mcp-personal-settings" class="section">
<h2><?php p($l->t('Astrolabe')); ?></h2>
<div class="mcp-settings-info">
<p><?php p($l->t('AI-powered semantic search across your Nextcloud content. Find documents by meaning, not just keywords.')); ?></p>
</div>
<!-- Service Status -->
<div class="mcp-status-card">
<h3><?php p($l->t('Service Status')); ?></h3>
<table class="mcp-info-table">
<tr>
<td><strong><?php p($l->t('Service URL')); ?></strong></td>
<td><code><?php p($_['serverUrl']); ?></code></td>
</tr>
<tr>
<td><strong><?php p($l->t('Version')); ?></strong></td>
<td><?php p($_['serverStatus']['version'] ?? 'Unknown'); ?></td>
</tr>
</table>
</div>
<!-- Indexing Status -->
<div class="mcp-status-card">
<h3><?php p($l->t('Content Indexing')); ?></h3>
<table class="mcp-info-table">
<tr>
<td><strong><?php p($l->t('Status')); ?></strong></td>
<td>
<?php if ($_['backgroundAccessGranted']): ?>
<span class="badge badge-success">
<span class="icon icon-checkmark-white"></span>
<?php p($l->t('Active')); ?>
</span>
<?php else: ?>
<span class="badge badge-neutral">
<?php p($l->t('Not Enabled')); ?>
</span>
<?php endif; ?>
</td>
</tr>
</table>
<?php if (!$_['backgroundAccessGranted']): ?>
<div class="mcp-grant-section">
<p class="mcp-help-text">
<?php p($l->t('Enable background indexing to use semantic search. Your Notes, Files, Calendar events, and Deck cards will be indexed so you can search by meaning.')); ?>
</p>
<a href="<?php p($_['serverUrl']); ?>/oauth/login?next=<?php p(urlencode($urlGenerator->getAbsoluteURL($urlGenerator->linkToRoute('settings.PersonalSettings.index', ['section' => 'astrolabe'])))); ?>" class="button primary" id="mcp-grant-button">
<span class="icon icon-confirm"></span>
<?php p($l->t('Enable Semantic Search')); ?>
</a>
</div>
<?php endif; ?>
<?php if ($_['backgroundAccessGranted'] && isset($_['session']['background_access_details'])): ?>
<div class="mcp-background-details">
<h4><?php p($l->t('Indexing Details')); ?></h4>
<table class="mcp-info-table">
<tr>
<td><strong><?php p($l->t('Enabled Since')); ?></strong></td>
<td><?php p($_['session']['background_access_details']['provisioned_at'] ?? 'N/A'); ?></td>
</tr>
<tr>
<td><strong><?php p($l->t('Indexed Content')); ?></strong></td>
<td><code style="font-size: 11px;"><?php p($_['session']['background_access_details']['scopes'] ?? 'N/A'); ?></code></td>
</tr>
</table>
<div class="mcp-revoke-section">
<form method="post" action="<?php p($urlGenerator->linkToRoute('astrolabe.api.revokeAccess')); ?>" id="mcp-revoke-form">
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>">
<button type="submit" class="button warning" id="mcp-revoke-button">
<span class="icon icon-delete"></span>
<?php p($l->t('Disable Indexing')); ?>
</button>
<p class="mcp-help-text">
<?php p($l->t('This will stop background indexing and remove your content from semantic search. You can re-enable it at any time.')); ?>
</p>
</form>
</div>
</div>
<?php endif; ?>
</div>
<!-- Identity Provider Profile -->
<?php if (isset($_['session']['idp_profile'])): ?>
<div class="mcp-status-card">
<h3><?php p($l->t('Identity Provider Profile')); ?></h3>
<table class="mcp-info-table">
<?php foreach ($_['session']['idp_profile'] as $key => $value): ?>
<tr>
<td><strong><?php p(ucfirst(str_replace('_', ' ', $key))); ?></strong></td>
<td>
<?php if (is_array($value)): ?>
<?php p(implode(', ', $value)); ?>
<?php else: ?>
<?php p((string)$value); ?>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
<?php endif; ?>
<!-- Semantic Search Features -->
<?php if ($_['vectorSyncEnabled']): ?>
<div class="mcp-status-card">
<h3><?php p($l->t('Search Your Content')); ?></h3>
<p><?php p($l->t('Use natural language to search across your Notes, Files, Calendar, and Deck cards. Ask questions like "meeting notes from last week" or "recipes with chicken".')); ?></p>
<a href="<?php p($urlGenerator->linkToRoute('astrolabe.page.index')); ?>" class="button primary">
<span class="icon icon-search"></span>
<?php p($l->t('Open Astrolabe')); ?>
</a>
</div>
<?php else: ?>
<div class="mcp-status-card mcp-disabled">
<h3><?php p($l->t('Semantic Search')); ?></h3>
<p class="mcp-help-text">
<?php p($l->t('Semantic search is not enabled on this server. Contact your administrator to enable this feature.')); ?>
</p>
</div>
<?php endif; ?>
<!-- Connection Management -->
<div class="mcp-status-card">
<h3><?php p($l->t('Manage Connection')); ?></h3>
<p><?php p($l->t('You are connected to the Astrolabe service.')); ?></p>
<div class="mcp-revoke-section">
<form method="post" action="<?php p($urlGenerator->linkToRoute('astrolabe.oauth.disconnect')); ?>" id="mcp-disconnect-form">
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>">
<button type="submit" class="button warning" id="mcp-disconnect-button">
<span class="icon icon-close"></span>
<?php p($l->t('Disconnect')); ?>
</button>
<p class="mcp-help-text">
<?php p($l->t('This will disconnect from the Astrolabe service. You will need to re-authorize to use semantic search features.')); ?>
</p>
</form>
</div>
</div>
</div>
<script>
// Confirm disable and disconnect actions
document.addEventListener('DOMContentLoaded', function() {
const revokeForm = document.getElementById('mcp-revoke-form');
if (revokeForm) {
revokeForm.addEventListener('submit', function(e) {
if (!confirm('<?php p($l->t('Are you sure you want to disable indexing? Your content will be removed from semantic search.')); ?>')) {
e.preventDefault();
}
});
}
const disconnectForm = document.getElementById('mcp-disconnect-form');
if (disconnectForm) {
disconnectForm.addEventListener('submit', function(e) {
if (!confirm('<?php p($l->t('Are you sure you want to disconnect from Astrolabe? You will need to re-authorize to use semantic search.')); ?>')) {
e.preventDefault();
}
});
}
});
</script>