Skip to content

Commit 2e2205e

Browse files
committed
feat: add section to summarize local endpoint in settings
1 parent e380629 commit 2e2205e

3 files changed

Lines changed: 132 additions & 7 deletions

File tree

src/localEndpoint.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,21 @@ export class LocalEndpoint {
2424
public readonly url = 'local://sparql-endpoint';
2525
private store: import('oxigraph').Store | null = null;
2626
private tripleCount = 0;
27-
private loadedFiles: string[] = [];
27+
private loadedFiles: vscode.Uri[] = [];
2828

2929
isLoaded(): boolean {
3030
return this.store !== null && this.tripleCount > 0;
3131
}
3232

33-
getInfo(): { triples: number; files: string[] } {
34-
return { triples: this.tripleCount, files: [...this.loadedFiles] };
33+
getInfo(): { triples: number; files: Array<{ label: string; uri: string }> } {
34+
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri;
35+
return {
36+
triples: this.tripleCount,
37+
files: this.loadedFiles.map((uri) => ({
38+
label: workspaceRoot ? vscode.workspace.asRelativePath(uri) : uri.fsPath,
39+
uri: uri.toString(),
40+
})),
41+
};
3542
}
3643

3744
async addFile(uri: vscode.Uri): Promise<number> {
@@ -58,17 +65,16 @@ export class LocalEndpoint {
5865
this.store.load(content, { format: mimeType, base_iri: uri.toString() });
5966
}
6067
this.tripleCount = this.store.size;
61-
const fileName = uri.path.split('/').pop() ?? uri.path;
62-
if (!this.loadedFiles.includes(fileName)) {
63-
this.loadedFiles.push(fileName);
68+
if (!this.loadedFiles.some((f) => f.toString() === uri.toString())) {
69+
this.loadedFiles.push(uri);
6470
}
6571
return this.tripleCount;
6672
}
6773

6874
reset(): void {
6975
this.store = null;
7076
this.tripleCount = 0;
71-
this.loadedFiles = [];
77+
this.loadedFiles = [] as vscode.Uri[];
7278
}
7379

7480
query(query: string, queryType: string): LocalQueryResult {

src/panels/settingsPanel.html

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,52 @@
433433
.ack a:hover {
434434
text-decoration: underline;
435435
}
436+
437+
/* Local endpoint info */
438+
.local-endpoint-info {
439+
border: 1px solid var(--border);
440+
border-radius: 4px;
441+
padding: 10px 14px;
442+
font-size: 0.88em;
443+
background: var(--input-bg);
444+
}
445+
.local-endpoint-stat {
446+
display: flex;
447+
align-items: baseline;
448+
gap: 6px;
449+
margin-bottom: 6px;
450+
}
451+
.local-endpoint-stat:last-child {
452+
margin-bottom: 0;
453+
}
454+
.local-endpoint-stat-label {
455+
color: var(--desc-fg);
456+
min-width: 70px;
457+
flex-shrink: 0;
458+
}
459+
.local-endpoint-stat-value {
460+
font-family: var(--vscode-editor-font-family, monospace);
461+
font-weight: 600;
462+
}
463+
.local-endpoint-files {
464+
display: flex;
465+
flex-direction: column;
466+
gap: 2px;
467+
margin-top: 2px;
468+
}
469+
.local-endpoint-file {
470+
font-family: var(--vscode-editor-font-family, monospace);
471+
font-size: 0.95em;
472+
color: var(--vscode-textLink-foreground);
473+
cursor: pointer;
474+
background: none;
475+
border: none;
476+
padding: 0;
477+
text-align: left;
478+
}
479+
.local-endpoint-file:hover {
480+
text-decoration: underline;
481+
}
436482
</style>
437483
</head>
438484
<body>
@@ -451,6 +497,11 @@ <h2>Configure Language Server</h2>
451497
</button>
452498
</p>
453499

500+
<div id="local-endpoint-section" style="display: none">
501+
<h2>Local Endpoint</h2>
502+
<div id="local-endpoint-body"></div>
503+
</div>
504+
454505
<h2>Acknowledgements</h2>
455506
<p class="ack">
456507
Diagnostics, autocomplete, and formatting powered by the
@@ -471,6 +522,7 @@ <h2>Acknowledgements</h2>
471522
const vscode = acquireVsCodeApi();
472523
const initialEndpointBackends = __ENDPOINT_BACKENDS__;
473524
let activeEndpointUrl = __ACTIVE_ENDPOINT__;
525+
let localEndpointInfo = __LOCAL_ENDPOINT_INFO__;
474526

475527
// ── SPARQL syntax highlighter ──────────────────────────────────────
476528

@@ -824,6 +876,61 @@ <h2>Acknowledgements</h2>
824876

825877
renderEndpoints(endpointBackends);
826878

879+
// ── Local endpoint info ────────────────────────────────────────────────
880+
881+
function renderLocalEndpoint(info) {
882+
const section = document.getElementById('local-endpoint-section');
883+
const body = document.getElementById('local-endpoint-body');
884+
if (!info || info.files.length === 0) {
885+
section.style.display = 'none';
886+
return;
887+
}
888+
section.style.display = '';
889+
body.innerHTML = '';
890+
891+
const card = document.createElement('div');
892+
card.className = 'local-endpoint-info';
893+
894+
// Triples row
895+
const triplesRow = document.createElement('div');
896+
triplesRow.className = 'local-endpoint-stat';
897+
const triplesLabel = document.createElement('span');
898+
triplesLabel.className = 'local-endpoint-stat-label';
899+
triplesLabel.textContent = 'Triples';
900+
const triplesValue = document.createElement('span');
901+
triplesValue.className = 'local-endpoint-stat-value';
902+
triplesValue.textContent = info.triples.toLocaleString();
903+
triplesRow.appendChild(triplesLabel);
904+
triplesRow.appendChild(triplesValue);
905+
card.appendChild(triplesRow);
906+
907+
// Files row
908+
const filesRow = document.createElement('div');
909+
filesRow.className = 'local-endpoint-stat';
910+
const filesLabel = document.createElement('span');
911+
filesLabel.className = 'local-endpoint-stat-label';
912+
filesLabel.textContent = 'Files';
913+
const filesList = document.createElement('div');
914+
filesList.className = 'local-endpoint-files';
915+
for (const file of info.files) {
916+
const btn = document.createElement('button');
917+
btn.className = 'local-endpoint-file';
918+
btn.textContent = file.label;
919+
btn.title = file.label;
920+
btn.addEventListener('click', () => {
921+
vscode.postMessage({ type: 'openLocalFile', uri: file.uri });
922+
});
923+
filesList.appendChild(btn);
924+
}
925+
filesRow.appendChild(filesLabel);
926+
filesRow.appendChild(filesList);
927+
card.appendChild(filesRow);
928+
929+
body.appendChild(card);
930+
}
931+
932+
renderLocalEndpoint(localEndpointInfo);
933+
827934
document.getElementById('open-settings-btn').addEventListener('click', () => {
828935
vscode.postMessage({ type: 'openVscodeSettings' });
829936
});
@@ -839,6 +946,10 @@ <h2>Acknowledgements</h2>
839946
Object.assign(endpointBackends, msg.endpointBackends);
840947
renderEndpoints(endpointBackends);
841948
}
949+
if (msg.localEndpointInfo !== undefined) {
950+
localEndpointInfo = msg.localEndpointInfo;
951+
renderLocalEndpoint(localEndpointInfo);
952+
}
842953
}
843954
});
844955
</script>

src/panels/settingsPanel.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from 'vscode';
22
import { getNonce } from '../utils';
33
import { type BackendConfig, ExtensionState } from '../state';
4+
import { localEndpoint } from '../localEndpoint';
45

56
export class SettingsPanel {
67
public static currentPanel: vscode.WebviewPanel | undefined;
@@ -17,6 +18,7 @@ export class SettingsPanel {
1718
type: 'set',
1819
endpointBackends: state.getBackends(),
1920
activeEndpointUrl: activeEndpointUrl ?? '',
21+
localEndpointInfo: localEndpoint.getInfo(),
2022
});
2123
return;
2224
}
@@ -36,6 +38,7 @@ export class SettingsPanel {
3638
context.extensionUri,
3739
state.getBackends(),
3840
activeEndpointUrl ?? '',
41+
localEndpoint.getInfo(),
3942
);
4043
panel.webview.onDidReceiveMessage(async (message) => {
4144
if (message.type === 'openExamplesForEndpoint') {
@@ -67,6 +70,9 @@ export class SettingsPanel {
6770
if (onSaveEndpointBackend) {
6871
await onSaveEndpointBackend(message.endpointUrl, message.config as BackendConfig);
6972
}
73+
} else if (message.type === 'openLocalFile') {
74+
const uri = vscode.Uri.parse(message.uri as string);
75+
await vscode.window.showTextDocument(uri);
7076
} else if (message.type === 'deleteEndpointBackend') {
7177
const backends = state.getBackends();
7278
delete backends[message.endpointUrl as string];
@@ -83,12 +89,14 @@ export class SettingsPanel {
8389
extensionUri: vscode.Uri,
8490
endpointBackends: Record<string, BackendConfig>,
8591
activeEndpointUrl: string,
92+
localEndpointInfo: { triples: number; files: Array<{ label: string; uri: string }> },
8693
): Promise<string> {
8794
const replacements: Record<string, string> = {
8895
__NONCE__: getNonce(),
8996
__CSP_SOURCE__: webview.cspSource,
9097
__ENDPOINT_BACKENDS__: JSON.stringify(endpointBackends ?? {}),
9198
__ACTIVE_ENDPOINT__: JSON.stringify(activeEndpointUrl),
99+
__LOCAL_ENDPOINT_INFO__: JSON.stringify(localEndpointInfo),
92100
};
93101
const htmlUri = vscode.Uri.joinPath(extensionUri, 'dist', 'panels', 'settingsPanel.html');
94102
const htmlBytes = await vscode.workspace.fs.readFile(htmlUri);

0 commit comments

Comments
 (0)