Files
gnome-jarvis/apps/gnome-extension/[email protected]/obsidian-settings.js
T
snxraven 08554c205c
Rolling release / release (push) Failing after 1m44s
Updates
2026-09-14 12:15:47 -04:00

78 lines
6.2 KiB
JavaScript

import Adw from 'gi://Adw';
import Gio from 'gi://Gio';
import Gtk from 'gi://Gtk';
import { daemonCall } from './settings-editor.js';
export function obsidianSettingsPage(editor, window) {
const page = editor.page(window, 'Obsidian', ['Obsidian vault'], 'folder-documents-symbolic');
const pathRow = editor.rows.get('obsidianVaultPath');
const choose = new Gtk.Button({ label: 'Choose folder…', valign: Gtk.Align.CENTER });
choose.connect('clicked', () => {
const dialog = new Gtk.FileChooserNative({ title: 'Choose an empty agent vault folder', transient_for: window, action: Gtk.FileChooserAction.SELECT_FOLDER, accept_label: 'Choose' });
dialog.connect('response', (_dialog, response) => {
if (response === Gtk.ResponseType.ACCEPT) {
const selected = dialog.get_file()?.get_path();
if (selected) pathRow.text = selected;
}
dialog.destroy();
});
dialog.show();
});
pathRow.add_suffix(choose);
const group = new Adw.PreferencesGroup({ title: 'Configuration and access checks', description: 'Enable vault and memory access on the Access page and save first. Initialize accepts only an empty folder or an existing Jarvis agent vault. Register the folder once using Open folder as vault in Obsidian.' });
const state = new Adw.ActionRow({ title: 'Bridge status', subtitle: 'Refresh to inspect the applied configuration.' });
const buttons = [];
const run = async args => {
if (Object.keys(editor.changes).some(key => key.startsWith('obsidian'))) throw new Error('Apply your Obsidian settings before using these controls.');
const [raw] = await daemonCall('ObsidianAction', '(s)', [JSON.stringify(args)]);
return JSON.parse(raw);
};
const describe = result => {
if (!result.enabled) return 'Disabled. Agent vault access is off.';
if (result.error) return `${result.path} · ${result.error}`;
return `${result.path} · ${result.ready ? 'Initialized' : 'Not initialized'} · Read ${result.readable ? 'OK' : 'unavailable'} · Write ${result.writable ? 'OK' : 'unavailable'} · Memory ${result.memoryEnabled ? (result.memoryVerified ? 'verified' : 'enabled') : 'off'} · ${result.files ?? 0} files${result.truncated ? ' (partial count)' : ''}${result.readWriteVerified ? ' · Read/write probe passed' : ''}`;
};
const action = (row, label, callback) => {
const button = new Gtk.Button({ label, valign: Gtk.Align.CENTER }); buttons.push(button);
button.connect('clicked', async () => {
for (const item of buttons) item.sensitive = false;
try { await callback(); } catch (error) { state.subtitle = error.message; }
finally { for (const item of buttons) item.sensitive = true; }
}); row.add_suffix(button);
};
action(state, 'Refresh', async () => { state.subtitle = describe(await run({ action: 'status' })); });
group.add(state);
const checks = new Adw.ActionRow({ title: 'Prepare and verify', subtitle: 'Verify writes, reads, and removes a temporary probe. Memory gets its own check when enabled.' });
action(checks, 'Initialize vault', async () => { state.subtitle = describe(await run({ action: 'initialize' })); });
action(checks, 'Verify access', async () => { state.subtitle = describe(await run({ action: 'verify' })); });
group.add(checks);
const open = new Adw.ActionRow({ title: 'Open vault', subtitle: 'Requires Obsidian installed and this folder registered as a vault. Folder opens the file manager.' });
action(open, 'Folder', async () => { const status = await run({ action: 'status' }); if (!status.ready) throw new Error(status.error || 'Enable and initialize the vault first'); Gio.AppInfo.launch_default_for_uri(Gio.File.new_for_path(status.path).get_uri(), null); });
action(open, 'Obsidian', async () => { const status = await run({ action: 'status' }); if (!status.uri) throw new Error(status.error || 'Enable and initialize the vault first'); Gio.AppInfo.launch_default_for_uri(status.uri, null); });
group.add(open); page.add(group);
const memory = new Adw.PreferencesGroup({ title: 'Inspect notes and memory', description: 'Browse files, search Markdown, or read a vault-relative path. Memory access follows the applied memory switch. Results stay in this window.' });
const input = new Adw.EntryRow({ title: 'Search text or relative file path' }); memory.add(input);
const inspect = new Adw.ActionRow({ title: 'Vault contents', subtitle: 'Read displays text files. Use the agent to create, edit, organize, or restore notes.' });
const view = new Gtk.TextView({ editable: false, cursor_visible: true, wrap_mode: Gtk.WrapMode.WORD_CHAR, left_margin: 12, right_margin: 12, top_margin: 8, bottom_margin: 8 });
const scroll = new Gtk.ScrolledWindow({ min_content_height: 220, max_content_height: 360, propagate_natural_height: true, has_frame: true }); scroll.set_child(view);
const show = value => view.buffer.set_text(String(value), -1);
let continuation = null;
const inspectPage = async args => {
const result = await run(args);
continuation = result.nextOffset != null ? { ...args, offset: result.nextOffset, ...(result.revision ? { revision: result.revision } : {}) } : null;
let text;
if (args.action === 'read') text = `${result.path} (byte ${result.offset})\n\n${result.content}`;
else if (args.action === 'list') text = result.entries.map(e => `${e.type === 'folder' ? '[folder]' : `${e.bytes} B`} ${e.path}`).join('\n');
else text = result.matches.map(e => `${e.path}\n${e.snippet}`).join('\n\n') || 'No matches on this page.';
show(text + (continuation ? '\n\nMore content available — press Next page.' : '\n\nEnd of results.'));
};
action(inspect, 'Files', async () => inspectPage({ action: 'list' }));
action(inspect, 'Search', async () => inspectPage({ action: 'search', query: input.text }));
action(inspect, 'Memory', async () => inspectPage({ action: 'memory_search', query: input.text }));
action(inspect, 'Read', async () => { if (!input.text.toLowerCase().endsWith('.md')) throw new Error('Enter a Markdown file path, such as memory/preferences.md'); await inspectPage({ action: 'read', path: input.text }); });
action(inspect, 'Next page', async () => { if (continuation) await inspectPage(continuation); });
memory.add(inspect); memory.add(scroll); page.add(memory);
return page;
}