Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to the Reactodia will be documented in this document.
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
#### 💅 Polish
- Extend SPARQL example connection dialog:
* optional HTTP Basic authentication (credentials are kept in tab-scoped session storage, not in the URL);
* optional restriction of all queries to one or more named graphs via SPARQL Protocol `default-graph-uri` parameters;
* saved and recent connections (endpoint, graphs, username – no passwords) persisted in local storage: an unnamed connection rotates through the most recent few, a named one is pinned permanently; activating one connects directly, or pre-fills the form when a password needs re-entering.

## [0.35.2] - 2026-08-08
#### 🐛 Fixed
Expand Down
26 changes: 18 additions & 8 deletions examples/resources/common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,14 @@ export function ExampleToolbarMenu() {
}

export function getHashQuery(): URLSearchParams | undefined {
const hash = window.location.hash;
if (hash.length > 1 && hash.includes('=')) {
// Parse the raw fragment from `href`: the `location.hash` getter returns it
// percent-decoded in Firefox, which corrupts values with encoded & or =
const href = window.location.href;
const hashIndex = href.indexOf('#');
const hash = hashIndex >= 0 ? href.substring(hashIndex + 1) : '';
if (hash.length > 0 && hash.includes('=')) {
try {
const hashQuery = new URLSearchParams(hash.substring(1));
const hashQuery = new URLSearchParams(hash);
return hashQuery;
} catch (e) {
/* ignore */
Expand All @@ -125,16 +129,22 @@ export function getHashQuery(): URLSearchParams | undefined {
return undefined;
}

export function setHashQueryParam(paramName: string, paramValue: string | null): void {
export function setHashQueryParams(params: { readonly [name: string]: string | null }): void {
const hashQuery = getHashQuery() ?? new URLSearchParams();
if (paramValue) {
hashQuery.set(paramName, paramValue);
} else {
hashQuery.delete(paramName);
for (const [paramName, paramValue] of Object.entries(params)) {
if (paramValue) {
hashQuery.set(paramName, paramValue);
} else {
hashQuery.delete(paramName);
}
}
window.location.hash = hashQuery.toString();
}

export function setHashQueryParam(paramName: string, paramValue: string | null): void {
setHashQueryParams({[paramName]: paramValue});
}

export function tryLoadLayoutFromLocalStorage(): Reactodia.SerializedDiagram | undefined {
let layoutKey: string | null = null;

Expand Down
Loading