From d24e04081fdc0ab0fa7160f282cc487a1b81d9d2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 09:35:52 +0000
Subject: [PATCH 1/6] Initial plan
From c6122046902b621fb632575b7b591876f26bdcf3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 09:45:45 +0000
Subject: [PATCH 2/6] docs: create comprehensive user and developer guides
Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com>
---
README.md | 279 +++---
docs/developer-guide.md | 2015 +++++++++++++++++++++++++++++++++++++++
docs/user-guide.md | 753 +++++++++++++++
3 files changed, 2891 insertions(+), 156 deletions(-)
create mode 100644 docs/developer-guide.md
create mode 100644 docs/user-guide.md
diff --git a/README.md b/README.md
index c71e21bc..543f940a 100644
--- a/README.md
+++ b/README.md
@@ -1,207 +1,174 @@
# YASGUI
-YASGUI (Yet Another SPARQL GUI) is an advanced SPARQL client for querying and exploring RDF data. It provides a user-friendly interface for writing SPARQL queries, executing them against SPARQL endpoints, and visualizing the results in various formats.
-
-Go to https://yasgui.matdata.eu/ and use it freely in production. Or fork this repo and extend it yourself. Contributions are certainly welcome!
-
-## Table of Contents
-
-- [Useful Links](#useful-links)
-- [Features](#features)
- - [Keyboard Shortcuts](#keyboard-shortcuts)
- - [Query Editor (Yasqe)](#query-editor-yasqe)
- - [Fullscreen Mode](#fullscreen-mode)
- - [URI Explorer](#uri-explorer)
- - [Prefix Management](#prefix-management)
- - [Result Visualization Plugins (Yasr)](#result-visualization-plugins-yasr)
-- [Installation](#installation)
- - [npm](#npm)
- - [Yarn](#yarn)
-- [Local Development](#local-development)
-- [License](#license)
-
-## Useful Links
-
-- Production environment: https://yasgui.matdata.eu/
-- Dev environment: https://yasgui-dev.matdata.eu/ (GitHub Pages - automatically updated with every commit to main branch)
-- User documentation: https://docs.triply.cc/yasgui/
-- Developer documentation: https://docs.triply.cc/yasgui-api/
-- Docker Hub: https://hub.docker.com/r/mathiasvda/yasgui
+**Yet Another SPARQL GUI (YASGUI)** is a powerful, user-friendly web-based interface for querying and exploring RDF data using SPARQL. It combines a feature-rich query editor (YASQE) with a versatile results viewer (YASR) to provide a comprehensive SPARQL IDE.
-## Features
-
-### Themes
-
-Yasgui supports both **light** and **dark** themes with instant switching:
-
-- **Theme Toggle Button**: Quickly switch between light and dark modes using the button in the tab bar
-- **Persistent Preference**: Your theme choice is automatically saved
-- **System Detection**: Automatically matches your system's dark/light mode preference
-- **Full Coverage**: Consistent theming across all components (editor, results, modals)
-
-See the [Theme Guide](./docs/THEME_GUIDE.md) for detailed configuration options and usage examples.
-
-See the [Theme Implementation Guide](./docs/THEME_IMPLEMENTATION_GUIDE.md) for detailed instructions on how to implement theme support in your own Yasgui plugins.
-
-### Layout Orientation
-
-Yasgui supports two layout orientations to optimize screen space usage:
-
-- **Vertical Layout (Default)**: YASQE (query editor) positioned above YASR (results viewer)
-- **Horizontal Layout**: YASQE and YASR positioned side-by-side
-
-The horizontal layout is particularly useful for wide monitors, allowing you to dedicate the complete height to both the query editor and results viewer.
-
-#### Runtime Toggle
+π **Try it now**: [https://yasgui.matdata.eu/](https://yasgui.matdata.eu/)
-Users can switch between vertical and horizontal layouts at any time using the **layout toggle button** in the control bar (next to the endpoint selector). This button shows:
-- **Side-by-side rectangles icon** when in vertical mode (click to switch to horizontal)
-- **Stacked rectangles icon** when in horizontal mode (click to switch to vertical)
-
-#### Initial Configuration
-
-You can also set the initial layout orientation when creating a Yasgui instance:
-
-```javascript
-new Yasgui(document.getElementById("yasgui"), {
- orientation: "horizontal" // or "vertical" (default)
-});
-```
-
-#### CSS Customization
-
-Customize the header height used in the horizontal layout calculation by setting the CSS custom property:
-
-```css
-.yasgui {
- --yasgui-header-height: 120px; /* Adjust based on your header height */
- --yasgui-min-height: 500px; /* Minimum height for horizontal layout panels */
-}
-```
+[](https://www.npmjs.com/package/@matdata/yasgui)
+[](https://opensource.org/licenses/MIT)
-### SPARQL Query Formatting
+---
-Yasqe now uses the [sparql-formatter](https://github.com/sparqling/sparql-formatter) library (replacing the legacy built-in formatter) to provide standards-compliant SPARQL formatting:
+## Quick Links
-- **Format Button**: Click the format button in the editor toolbar to instantly format your query
-- **Keyboard Shortcut**: Use **Shift+Ctrl+F** to format the current query
-- **Formatter Selection**: Choose between `sparql-formatter` (default) or the legacy formatter in the Settings modal
-- **Auto-format on Query Execution**: Enable automatic formatting before query execution in the Settings modal
+- π **[User Guide](./docs/user-guide.md)** - Complete guide for end users
+- π οΈ **[Developer Guide](./docs/developer-guide.md)** - API reference and integration guide
+- π **[Production Environment](https://yasgui.matdata.eu/)** - Live instance
+- π¦ **[npm Package](https://www.npmjs.com/package/@matdata/yasgui)**
+- π³ **[Docker Hub](https://hub.docker.com/r/mathiasvda/yasgui)**
+- π **[Releases & Changelog](https://github.com/Matdata-eu/Yasgui/releases)**
+- π» **[GitHub Repository](https://github.com/Matdata-eu/Yasgui)**
-Both formatter options and auto-format settings are saved persistently in your browser's local storage.
+---
-### Keyboard Shortcuts
+## Documentation
-#### Query Editor (Yasqe)
-- **Ctrl+Enter** / **Cmd+Enter**: Execute the current query
-- **Ctrl+Space**: Trigger autocomplete
-- **Ctrl+S**: Save query to local storage
-- **Shift+Ctrl+F**: Format query (uses selected formatter from Settings)
-- **Ctrl+/**: Comment/uncomment selected lines
-- **Shift+Ctrl+D**: Duplicate the current line
-- **Shift+Ctrl+K**: Delete the current line
-- **Esc**: Remove focus from the editor
+The **documentation for YASGUI is now hosted on GitHub Pages**:
-#### Fullscreen Mode
-- **F11**: Toggle fullscreen mode for the query editor (Yasqe)
-- **F10**: Toggle fullscreen mode for the results viewer (Yasr)
-- **F9**: Switch between Yasqe and Yasr fullscreen modes
+- **Documentation Website**: [https://matdata-eu.github.io/Yasgui/](https://matdata-eu.github.io/Yasgui/) (coming soon with Docusaurus)
+- **Development Environment**: [https://matdata-eu.github.io/Yasgui/dev/main/](https://matdata-eu.github.io/Yasgui/dev/main/) - Live build from the main branch
-#### URI Explorer
-- **Ctrl+Click** on any URI in the query editor: Automatically executes a CONSTRUCT query to explore the clicked URI's connections (incoming and outgoing triples). The query runs in the background without modifying your current query in the editor.
+The documentation is version-tagged with the repository, ensuring consistency between code and docs.
-### Prefix Management
-- **PREFIX Button**: Insert saved prefix declarations into your query (replaces existing PREFIX lines at the beginning)
-- **Settings Modal**: Access via the settings button (β) to manage:
- - **Saved Prefixes**: Define reusable PREFIX declarations
- - **Auto-capture**: Automatically captures new prefixes from your queries (enabled by default)
- - **Request Configuration**: Configure HTTP request method, accept headers, arguments, headers, and named/default graphs
-- **Default Prefixes**: Automatically includes `rdf:` and `rdfs:` prefixes for new users
-- **Prefix Autocomplete**: When typing a prefix declaration (e.g., `PREFIX foaf:`), the editor automatically queries [prefix.cc](https://prefix.cc) to suggest and auto-complete the full URI commonly associated with that prefix. This helps you quickly add standard prefixes without needing to remember their full URIs.
-
-### Endpoint Quick Switch Buttons
-
-Configure predefined SPARQL endpoint buttons for quick switching between different endpoints:
-
-- **Predefined Buttons**: Configure a list of endpoint buttons during Yasgui initialization
-- **User-Defined Buttons**: Users can add their own custom endpoint buttons through the Settings modal
-- **One-Click Switching**: Instantly switch to a different SPARQL endpoint with a single button click
-- **Persistent Custom Buttons**: User-defined buttons are saved in local storage
-- **Fully Themed**: Buttons automatically adapt to light and dark themes
-
-For detailed configuration options and examples, see the [Endpoint Buttons Guide](./docs/ENDPOINT_BUTTONS.md).
-
-### Result Visualization Plugins (Yasr)
-
-Yasgui includes several built-in plugins to visualize SPARQL query results:
-
-- **Table**: Interactive table view with sorting, filtering, pagination, and column resizing. Ideal for SELECT query results.
-- **Boolean**: Displays boolean results (true/false) with visual indicators. Automatically used for ASK queries.
-- **Response**: Raw response viewer with syntax highlighting and code folding. Shows the original response from the endpoint in JSON, XML, Turtle, or other formats.
-- **Geo**: Geographic visualization plugin for displaying spatial data on interactive maps. Visualizes geospatial triples with coordinates. See [Yasgui Geo TG Plugin](https://github.com/Thib-G/yasgui-geo-tg) for more details.
-- **Graph**: Visual graph representation of RDF data using nodes and edges. Ideal for CONSTRUCT/DESCRIBE query results. See [Yasgui Graph Plugin](https://github.com/Matdata-eu/yasgui-graph-plugin) for more details.
-- **Error**: Displays error messages and diagnostics when queries fail, including CORS troubleshooting guidance.
+## Features
-Plugins are automatically selected based on the query type and response format. You can manually switch between available plugins using the view selector in the results pane.
+YASGUI provides a complete SPARQL development environment with powerful features:
+
+### π¨ Themes & Layouts
+- **[Light & Dark Themes](./docs/user-guide.md#themes)** - Seamless theme switching with persistent preferences
+- **[Flexible Layouts](./docs/user-guide.md#layout-orientation)** - Vertical or horizontal editor/results arrangement
+- **System Detection** - Automatically matches your OS theme preference
+
+### βοΈ Advanced Query Editor
+- **[SPARQL Syntax Highlighting](./docs/user-guide.md#yasqe-query-editor)** - Color-coded SPARQL with error detection
+- **[Smart Autocomplete](./docs/user-guide.md#prefix-management)** - Context-aware suggestions for keywords, prefixes, and URIs
+- **[Query Formatting](./docs/user-guide.md#query-formatting)** - One-click query beautification with configurable formatters
+- **[Prefix Management](./docs/user-guide.md#prefix-management)** - Auto-capture and reuse PREFIX declarations
+- **[URI Explorer](./docs/user-guide.md#uri-explorer)** - Ctrl+Click URIs to explore connections
+- **[Keyboard Shortcuts](./docs/user-guide.md#keyboard-shortcuts)** - Efficient query development workflow
+
+### π Powerful Visualizations
+- **[Table Plugin](./docs/user-guide.md#table-plugin)** - Sortable, filterable, paginated result tables
+- **[Graph Plugin](./docs/user-guide.md#graph-plugin)** - Interactive RDF graph visualization
+- **[Geo Plugin](./docs/user-guide.md#geo-plugin)** - Geographic data on interactive maps
+- **[Response Plugin](./docs/user-guide.md#response-plugin)** - Raw response viewer with syntax highlighting
+- **[Boolean Plugin](./docs/user-guide.md#boolean-plugin)** - Visual true/false indicators for ASK queries
+- **[Error Plugin](./docs/user-guide.md#error-plugin)** - Detailed error diagnostics
+
+### π§ Developer Features
+- **[Multiple Tabs](./docs/user-guide.md#query-tabs)** - Work on multiple queries simultaneously
+- **[Endpoint Management](./docs/user-guide.md#endpoint-quick-switch)** - Quick-switch between SPARQL endpoints
+- **[Persistent Storage](./docs/user-guide.md#query-history-and-persistence)** - Auto-save queries and preferences
+- **[URL Sharing](./docs/user-guide.md#share-queries)** - Share queries via URL parameters
+- **[Fullscreen Mode](./docs/user-guide.md#fullscreen-mode)** - Maximize editor or results viewer
+- **[Export Results](./docs/developer-guide.md#yasr-class)** - Download results in various formats
+
+For detailed feature documentation, see the **[User Guide](./docs/user-guide.md)**.
## Installation
-Below are instructions on how to include Yasgui in your project.
-If you only want to install Yasr or Yasqe, replace yasgui in the commands below.
-
### npm
-```sh
-npm i @matdata/yasgui
+```bash
+npm install @matdata/yasgui
```
### Yarn
-```sh
+```bash
yarn add @matdata/yasgui
```
-### Docker
+### CDN
-Yasgui is also available as a Docker image on Docker Hub. Images are built and published automatically when a new release is created.
+```html
+
+
+```
+
+### Docker
-```sh
+```bash
docker pull mathiasvda/yasgui:latest
docker run -p 8080:8080 mathiasvda/yasgui:latest
```
-You can customize the default SPARQL endpoint by setting the `YASGUI_DEFAULT_ENDPOINT` environment variable:
-
-```sh
+**Custom endpoint:**
+```bash
docker run -p 8080:8080 -e YASGUI_DEFAULT_ENDPOINT=https://your-endpoint.com/sparql mathiasvda/yasgui:latest
```
-The application will be available at `http://localhost:8080`.
+For detailed installation instructions and usage examples, see the **[Developer Guide](./docs/developer-guide.md#installation)**.
+
+## Quick Start
+
+### Basic HTML Usage
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### ES Modules / React / Vue / Angular
-## Local Development
+```javascript
+import Yasgui from '@matdata/yasgui';
+import '@matdata/yasgui/build/yasgui.min.css';
+
+const yasgui = new Yasgui(document.getElementById('yasgui'), {
+ requestConfig: {
+ endpoint: 'https://query.wikidata.org/sparql'
+ },
+ theme: 'dark',
+ orientation: 'horizontal'
+});
+```
-#### Installing dependencies
+For framework-specific examples and advanced usage, see the **[Developer Guide](./docs/developer-guide.md#usage-examples)**.
-Run `npm install`.
+---
-#### Running Yasgui locally
+## Contributing
-To develop locally, run `npm run dev`
+We welcome contributions! To get started:
-Go to `http://localhost:5173/demo` in your browser to see Yasgui in action.
+1. Fork the repository
+2. Clone and install: `npm install`
+3. Run dev server: `npm run dev`
+4. Make your changes
+5. Run tests: `npm test`
+6. Submit a pull request
-#### Compiling Yasgui
+For detailed contribution guidelines, see the **[Developer Guide](./docs/developer-guide.md#contributing)**.
-Run `npm run build`. It'll store the transpiled js/css files in the `build` directory.
+---
## License
+MIT License - see [LICENSE](./LICENSE) file for details.
+
This is a fork from [Zazuko](https://github.com/zazuko/Yasgui) who forked it from [Triply](https://github.com/TriplyDB/Yasgui).
-This code is released under the MIT license.
+---
-## Release notes
+## Release Notes & Changelog
-Release notes can be found in the release section of the GitHub repository: [Yasgui Releases](https://github.com/Matdata-eu/yasgui/releases)
+Release notes and changelog are available in the [Releases](https://github.com/Matdata-eu/Yasgui/releases) section.
-Instructions on how to write release notes are found in
\ No newline at end of file
+For instructions on writing release notes, see [RELEASE_NOTES_INSTRUCTIONS.md](./docs/RELEASE_NOTES_INSTRUCTIONS.md)
\ No newline at end of file
diff --git a/docs/developer-guide.md b/docs/developer-guide.md
new file mode 100644
index 00000000..931f0391
--- /dev/null
+++ b/docs/developer-guide.md
@@ -0,0 +1,2015 @@
+# YASGUI Developer Guide
+
+This comprehensive guide covers everything developers need to know to integrate, customize, and extend YASGUI (Yet Another SPARQL GUI).
+
+## Table of Contents
+
+1. [Architecture Overview](#architecture-overview)
+2. [Installation](#installation)
+3. [Usage Examples](#usage-examples)
+4. [Configuration](#configuration)
+5. [API Reference](#api-reference)
+6. [Events](#events)
+7. [Plugin Development](#plugin-development)
+8. [Contributing](#contributing)
+
+---
+
+## Architecture Overview
+
+YASGUI is built as a monorepo with four main packages, each serving a specific purpose in the SPARQL querying workflow.
+
+### Package Structure
+
+```
+@matdata/yasgui (root)
+βββ @matdata/yasgui-utils - Shared utilities
+βββ @matdata/yasqe - SPARQL Query Editor
+βββ @matdata/yasr - SPARQL Results Viewer
+βββ @matdata/yasgui - Main integration package
+```
+
+### Architecture Diagram
+
+```
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β YASGUI β
+β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
+β β Tab Management Layer β β
+β β - Multiple query tabs β β
+β β - Tab persistence β β
+β β - Theme management β β
+β β - Settings & configuration β β
+β βββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
+β β
+β ββββββββββββββββββββββ βββββββββββββββββββββββ β
+β β YASQE β β YASR β β
+β β (Query Editor) β β (Results Viewer) β β
+β β β β β β
+β β ββββββββββββββββ β β ββββββββββββββββ β β
+β β β CodeMirror β β β β Plugins β β β
+β β β - Syntax β β β β - Table β β β
+β β β - Validation β β β β - Graph β β β
+β β β - Autocompleteβ β β β - Geo β β β
+β β ββββββββββββββββ β β β - Boolean β β β
+β β β β β - Response β β β
+β β ββββββββββββββββ β β β - Error β β β
+β β β SPARQL β β β ββββββββββββββββ β β
+β β β Execution β ββββββββββΆβ β β
+β β ββββββββββββββββ β β ββββββββββββββββ β β
+β β β β β Parser β β β
+β β ββββββββββββββββ β β β - JSON β β β
+β β β Prefixes β β β β - XML β β β
+β β β Management β β β β - CSV β β β
+β β ββββββββββββββββ β β β - Turtle β β β
+β ββββββββββββββββββββββ β ββββββββββββββββ β β
+β βββββββββββββββββββββββ β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+ β
+ β Uses
+ βΌ
+ ββββββββββββββββββββ
+ β YASGUI-UTILS β
+ β - DOM helpers β
+ β - Storage β
+ β - Utilities β
+ ββββββββββββββββββββ
+```
+
+### Package Details
+
+#### @matdata/yasgui-utils
+
+**Purpose**: Shared utility functions and helpers used across all packages.
+
+**Key Features:**
+- DOM manipulation utilities
+- Local storage abstraction
+- Common helper functions
+- SVG icon rendering
+- DOMPurify integration for XSS protection
+
+**Exports:**
+- `Storage`: localStorage abstraction with namespacing
+- `addClass`, `removeClass`, `hasClass`: DOM class utilities
+- `drawSvgStringAsElement`: SVG helper
+- `drawFontAwesomeIconAsSvg`: Icon rendering
+
+#### @matdata/yasqe (SPARQL Query Editor)
+
+**Purpose**: Rich SPARQL query editor built on CodeMirror.
+
+**Key Features:**
+- SPARQL syntax highlighting
+- Auto-completion (keywords, prefixes, properties, classes)
+- Query validation with error highlighting
+- Prefix management and auto-completion
+- Query execution
+- Query formatting (sparql-formatter integration)
+- Persistent storage
+- Keyboard shortcuts
+
+**Core Components:**
+- CodeMirror editor with SPARQL mode
+- SPARQL tokenizer/grammar
+- Autocomplete system (extensible)
+- HTTP request handler
+- Prefix utilities
+
+#### @matdata/yasr (SPARQL Results Viewer)
+
+**Purpose**: Flexible results visualization with plugin system.
+
+**Key Features:**
+- Plugin-based architecture
+- Multiple built-in visualization plugins
+- Result parsing (JSON, XML, CSV, Turtle)
+- Export functionality
+- Persistent plugin selection
+- Theme support
+
+**Core Components:**
+- Plugin manager
+- Result parsers
+- Plugin API
+- Header controls
+- Download manager
+
+#### @matdata/yasgui (Main Package)
+
+**Purpose**: Integrates YASQE and YASR into a complete SPARQL IDE.
+
+**Key Features:**
+- Tab management (multiple queries)
+- Endpoint management and quick-switch buttons
+- Theme system (light/dark)
+- Layout orientation (vertical/horizontal)
+- Settings modal
+- Tab persistence
+- Event system
+- URL-based query sharing
+
+**Core Components:**
+- Tab manager
+- Theme manager
+- Persistent configuration
+- Settings modal
+- Endpoint selector
+- Tab context menu
+
+---
+
+## Installation
+
+### npm
+
+Install the main package (includes all components):
+
+```bash
+npm install @matdata/yasgui
+```
+
+Install individual packages:
+
+```bash
+npm install @matdata/yasqe # Query editor only
+npm install @matdata/yasr # Results viewer only
+npm install @matdata/yasgui-utils # Utilities only
+```
+
+### Yarn
+
+```bash
+yarn add @matdata/yasgui
+```
+
+### CDN
+
+Include YASGUI directly from a CDN (replace `VERSION` with the desired version):
+
+```html
+
+
+
+
+
+```
+
+**Latest version:**
+```html
+
+
+```
+
+### Source
+
+Clone and build from source:
+
+```bash
+git clone https://github.com/Matdata-eu/Yasgui.git
+cd Yasgui
+npm install
+npm run build
+```
+
+Build output is in the `build/` directory.
+
+---
+
+## Usage Examples
+
+### Plain HTML
+
+Basic integration in a static HTML page:
+
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Node.js / ES Modules
+
+Using YASGUI in a Node.js application or with module bundlers:
+
+```javascript
+import Yasgui from '@matdata/yasgui';
+import '@matdata/yasgui/build/yasgui.min.css';
+
+const yasgui = new Yasgui(document.getElementById('yasgui'), {
+ requestConfig: {
+ endpoint: 'https://query.wikidata.org/sparql'
+ },
+ theme: 'dark',
+ orientation: 'horizontal'
+});
+```
+
+### CommonJS
+
+```javascript
+const Yasgui = require('@matdata/yasgui');
+require('@matdata/yasgui/build/yasgui.min.css');
+
+const yasgui = new Yasgui(document.getElementById('yasgui'));
+```
+
+### React
+
+Integration with React:
+
+```jsx
+import React, { useEffect, useRef } from 'react';
+import Yasgui from '@matdata/yasgui';
+import '@matdata/yasgui/build/yasgui.min.css';
+
+function YasguiComponent() {
+ const containerRef = useRef(null);
+ const yasguiRef = useRef(null);
+
+ useEffect(() => {
+ if (containerRef.current && !yasguiRef.current) {
+ yasguiRef.current = new Yasgui(containerRef.current, {
+ requestConfig: {
+ endpoint: 'https://dbpedia.org/sparql'
+ }
+ });
+
+ // Listen to events
+ yasguiRef.current.on('query', (instance, tab) => {
+ console.log('Query executed on tab:', tab.getName());
+ });
+ }
+
+ // Cleanup
+ return () => {
+ // Yasgui doesn't require explicit cleanup
+ // but you can add custom cleanup logic here
+ };
+ }, []);
+
+ return ;
+}
+
+export default YasguiComponent;
+```
+
+### Vue
+
+Integration with Vue 3:
+
+```vue
+
+
+
+
+
+
+
+```
+
+### Angular
+
+Integration with Angular:
+
+```typescript
+// yasgui.component.ts
+import { Component, OnInit, ElementRef, ViewChild } from '@angular/core';
+import Yasgui from '@matdata/yasgui';
+import '@matdata/yasgui/build/yasgui.min.css';
+
+@Component({
+ selector: 'app-yasgui',
+ template: '',
+ styleUrls: ['./yasgui.component.css']
+})
+export class YasguiComponent implements OnInit {
+ @ViewChild('yasguiContainer', { static: true })
+ yasguiContainer!: ElementRef;
+
+ private yasgui: any;
+
+ ngOnInit(): void {
+ this.yasgui = new Yasgui(this.yasguiContainer.nativeElement, {
+ requestConfig: {
+ endpoint: 'https://dbpedia.org/sparql'
+ }
+ });
+
+ this.yasgui.on('query', (instance: any, tab: any) => {
+ console.log('Query executed:', tab.getName());
+ });
+ }
+
+ ngOnDestroy(): void {
+ // Cleanup
+ this.yasgui = null;
+ }
+}
+```
+
+### Using YASQE and YASR Separately
+
+You can use the query editor and results viewer independently:
+
+```javascript
+import Yasqe from '@matdata/yasqe';
+import Yasr from '@matdata/yasr';
+import '@matdata/yasqe/build/yasqe.min.css';
+import '@matdata/yasr/build/yasr.min.css';
+
+// Create query editor
+const yasqe = new Yasqe(document.getElementById('yasqe'), {
+ value: 'SELECT * WHERE { ?s ?p ?o } LIMIT 10',
+ requestConfig: {
+ endpoint: 'https://dbpedia.org/sparql'
+ }
+});
+
+// Create results viewer
+const yasr = new Yasr(document.getElementById('yasr'), {
+ prefixes: yasqe.getPrefixes()
+});
+
+// Connect them
+yasqe.on('queryResponse', (yasqe, response, duration) => {
+ yasr.setResponse(response, duration);
+});
+
+// Execute query
+yasqe.query();
+```
+
+---
+
+## Configuration
+
+YASGUI offers extensive configuration options to customize behavior and appearance.
+
+### YASGUI Configuration
+
+Complete configuration object with all available options:
+
+```typescript
+interface Config {
+ // Auto-focus editor on load or tab switch
+ autofocus: boolean; // default: true
+
+ // Custom endpoint info renderer
+ endpointInfo?: (tab?: Tab) => Element;
+
+ // Copy endpoint to new tabs
+ copyEndpointOnNewTab: boolean; // default: true
+
+ // Default tab name
+ tabName: string; // default: "Query"
+
+ // CORS proxy URL
+ corsProxy?: string;
+
+ // Endpoint catalogue configuration
+ endpointCatalogueOptions: EndpointSelectConfig;
+
+ // Quick-switch endpoint buttons
+ endpointButtons?: EndpointButton[];
+
+ // Populate config from URL parameters
+ populateFromUrl: boolean | ((config: TabJson) => TabJson); // default: true
+
+ // Auto-create first tab on init
+ autoAddOnInit: boolean; // default: true
+
+ // Persistence ID for localStorage
+ persistenceId: string | ((yasgui: Yasgui) => string) | null;
+
+ // Persistence labels
+ persistenceLabelConfig: string; // default: "config"
+ persistenceLabelResponse: string; // default: "response"
+
+ // Persistence expiry (seconds)
+ persistencyExpire: number; // default: 2592000 (30 days)
+
+ // YASQE configuration
+ yasqe: Partial;
+
+ // YASR configuration
+ yasr: YasrConfig;
+
+ // Request configuration
+ requestConfig: RequestConfig;
+
+ // Context menu container element
+ contextMenuContainer?: HTMLElement;
+
+ // Non-SSL domain for mixed content
+ nonSslDomain?: string;
+
+ // Theme: 'light' or 'dark'
+ theme?: 'light' | 'dark';
+
+ // Show theme toggle button
+ showThemeToggle?: boolean; // default: true
+
+ // Layout orientation: 'vertical' or 'horizontal'
+ orientation?: 'vertical' | 'horizontal'; // default: 'vertical'
+}
+```
+
+#### Example Configuration
+
+```javascript
+const yasgui = new Yasgui(document.getElementById('yasgui'), {
+ // Basic settings
+ autofocus: true,
+ tabName: 'My Query',
+ theme: 'dark',
+ orientation: 'horizontal',
+
+ // Endpoint configuration
+ requestConfig: {
+ endpoint: 'https://dbpedia.org/sparql',
+ method: 'POST'
+ },
+
+ // Quick-switch buttons
+ endpointButtons: [
+ { endpoint: 'https://dbpedia.org/sparql', label: 'DBpedia' },
+ { endpoint: 'https://query.wikidata.org/sparql', label: 'Wikidata' }
+ ],
+
+ // Persistence
+ persistenceId: 'my-yasgui-instance',
+ persistencyExpire: 60 * 60 * 24 * 7, // 7 days
+
+ // YASQE config
+ yasqe: {
+ value: 'SELECT * WHERE { ?s ?p ?o } LIMIT 10',
+ lineNumbers: true,
+ showQueryButton: true
+ },
+
+ // YASR config
+ yasr: {
+ defaultPlugin: 'table',
+ maxPersistentResponseSize: 500000
+ }
+});
+```
+
+### YASQE Configuration
+
+YASQE (editor) specific configuration options:
+
+```typescript
+interface YasqeConfig {
+ // Initial query value
+ value?: string;
+
+ // CodeMirror mode
+ mode: string; // default: 'sparql11'
+
+ // Theme
+ theme: string; // default: 'default'
+
+ // Line numbers
+ lineNumbers: boolean; // default: true
+
+ // Line wrapping
+ lineWrapping: boolean; // default: false
+
+ // Tab size
+ tabSize: number; // default: 2
+
+ // Indent unit
+ indentUnit: number; // default: 2
+
+ // Auto close brackets
+ autoCloseBrackets: boolean; // default: true
+
+ // Match brackets
+ matchBrackets: boolean; // default: true
+
+ // Fold gutter (code folding)
+ foldGutter: boolean; // default: true
+
+ // Show query button
+ showQueryButton: boolean; // default: true
+
+ // Show share button
+ showShareButton: boolean; // default: false
+
+ // Persistent storage ID
+ persistenceId?: string | ((yasqe: Yasqe) => string);
+
+ // Request configuration
+ requestConfig: RequestConfig;
+
+ // Autocomplete configuration
+ autocomplete: {
+ enabled: boolean;
+ completerConfigs: CompleterConfig[];
+ };
+
+ // Editor height
+ editorHeight: string; // default: '300px'
+
+ // Resizable editor
+ resizeable: boolean; // default: true
+}
+```
+
+#### YASQE Example
+
+```javascript
+import Yasqe from '@matdata/yasqe';
+
+const yasqe = new Yasqe(document.getElementById('yasqe'), {
+ value: `PREFIX rdf:
+PREFIX rdfs:
+
+SELECT ?label WHERE {
+ ?s rdfs:label ?label
+} LIMIT 10`,
+
+ theme: 'material-palenight',
+ lineNumbers: true,
+ lineWrapping: false,
+ showQueryButton: true,
+ editorHeight: '400px',
+
+ requestConfig: {
+ endpoint: 'https://dbpedia.org/sparql',
+ method: 'POST',
+ headers: {
+ 'Accept': 'application/sparql-results+json'
+ }
+ },
+
+ autocomplete: {
+ enabled: true,
+ completerConfigs: [
+ { name: 'prefixes', enabled: true },
+ { name: 'properties', enabled: true },
+ { name: 'classes', enabled: true }
+ ]
+ }
+});
+```
+
+### YASR Configuration
+
+YASR (results viewer) specific configuration options:
+
+```typescript
+interface YasrConfig {
+ // Default plugin to use
+ defaultPlugin?: string;
+
+ // Plugin configurations
+ plugins: {
+ [pluginName: string]: {
+ // Plugin-specific options
+ // Priority for plugin selection (higher = preferred)
+ priority?: number;
+ };
+ };
+
+ // Prefixes for result display
+ prefixes?: Prefixes;
+
+ // Max response size to persist (bytes)
+ maxPersistentResponseSize: number; // default: 500000
+
+ // Persistent storage ID
+ persistenceId?: string | ((yasr: Yasr) => string);
+
+ // Persistence labels
+ persistenceLabelConfig: string;
+ persistenceLabelResponse: string;
+
+ // Download filename
+ downloadFilename?: string | ((yasr: Yasr) => string);
+
+ // Error renderers
+ errorRenderers?: ErrorRenderer[];
+}
+```
+
+#### YASR Example
+
+```javascript
+import Yasr from '@matdata/yasr';
+
+const yasr = new Yasr(document.getElementById('yasr'), {
+ defaultPlugin: 'table',
+
+ plugins: {
+ table: {
+ priority: 10,
+ pageSize: 50
+ },
+ graph: {
+ priority: 5
+ },
+ response: {
+ priority: 1
+ }
+ },
+
+ prefixes: {
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
+ foaf: 'http://xmlns.com/foaf/0.1/'
+ },
+
+ maxPersistentResponseSize: 1000000, // 1MB
+
+ downloadFilename: (yasr) => {
+ return `results-${new Date().toISOString()}.csv`;
+ }
+});
+```
+
+### Request Configuration
+
+HTTP request configuration for SPARQL queries:
+
+```typescript
+interface RequestConfig {
+ // SPARQL endpoint URL
+ endpoint: string;
+
+ // HTTP method
+ method: 'GET' | 'POST'; // default: 'POST'
+
+ // Accept header
+ acceptHeaderGraph?: string;
+ acceptHeaderSelect?: string;
+ acceptHeaderUpdate?: string;
+
+ // Named graphs
+ namedGraphs?: string[];
+
+ // Default graphs
+ defaultGraphs?: string[];
+
+ // Custom headers
+ headers?: { [key: string]: string };
+
+ // Custom arguments (URL parameters)
+ args?: Array<{ name: string; value: string }>;
+
+ // Adjust query before request
+ adjustQueryBeforeRequest?: (query: string) => string;
+
+ // CORS proxy
+ corsProxy?: string;
+}
+```
+
+#### Request Configuration Example
+
+```javascript
+const config = {
+ requestConfig: {
+ endpoint: 'https://dbpedia.org/sparql',
+ method: 'POST',
+
+ acceptHeaderSelect: 'application/sparql-results+json',
+ acceptHeaderGraph: 'text/turtle',
+
+ namedGraphs: ['http://dbpedia.org'],
+
+ headers: {
+ 'User-Agent': 'MyApp/1.0'
+ },
+
+ args: [
+ { name: 'timeout', value: '30000' },
+ { name: 'debug', value: 'on' }
+ ],
+
+ adjustQueryBeforeRequest: (query) => {
+ // Add timestamp comment
+ return `# Query executed at ${new Date().toISOString()}\n${query}`;
+ }
+ }
+};
+```
+
+### Endpoint Buttons Configuration
+
+Configure quick-switch endpoint buttons:
+
+```typescript
+interface EndpointButton {
+ endpoint: string; // SPARQL endpoint URL
+ label: string; // Button text
+}
+```
+
+```javascript
+const yasgui = new Yasgui(element, {
+ endpointButtons: [
+ { endpoint: 'https://dbpedia.org/sparql', label: 'DBpedia' },
+ { endpoint: 'https://query.wikidata.org/sparql', label: 'Wikidata' },
+ { endpoint: 'https://lod.openaire.eu/sparql', label: 'OpenAIRE' }
+ ]
+});
+```
+
+### Theme Configuration
+
+Configure theme settings:
+
+```javascript
+// Set initial theme
+const yasgui = new Yasgui(element, {
+ theme: 'dark', // 'light' or 'dark'
+ showThemeToggle: true // Show/hide theme toggle button
+});
+
+// Programmatic theme control
+yasgui.setTheme('dark');
+const current = yasgui.getTheme(); // Returns 'light' or 'dark'
+yasgui.toggleTheme(); // Switch theme
+```
+
+---
+
+## API Reference
+
+### Yasgui Class
+
+Main YASGUI instance.
+
+#### Constructor
+
+```typescript
+new Yasgui(parent: HTMLElement, config?: PartialConfig)
+```
+
+**Parameters:**
+- `parent`: DOM element to attach YASGUI to
+- `config`: Optional configuration object
+
+**Example:**
+```javascript
+const yasgui = new Yasgui(document.getElementById('yasgui'), {
+ requestConfig: { endpoint: 'https://dbpedia.org/sparql' }
+});
+```
+
+#### Methods
+
+##### `getTab(tabId?: string): Tab | undefined`
+
+Get a tab instance by ID. If no ID provided, returns current tab.
+
+```javascript
+const currentTab = yasgui.getTab();
+const specificTab = yasgui.getTab('tab-123');
+```
+
+##### `addTab(select?: boolean, config?: PartialTabConfig): Tab`
+
+Add a new query tab.
+
+**Parameters:**
+- `select`: Whether to select the new tab (default: false)
+- `config`: Optional tab configuration
+
+```javascript
+const newTab = yasgui.addTab(true, {
+ name: 'My Query',
+ yasqe: {
+ value: 'SELECT * WHERE { ?s ?p ?o } LIMIT 10'
+ }
+});
+```
+
+##### `selectTabId(tabId: string): void`
+
+Select a tab by ID.
+
+```javascript
+yasgui.selectTabId('tab-123');
+```
+
+##### `closeTab(tab: Tab): void`
+
+Close a tab.
+
+```javascript
+const tab = yasgui.getTab();
+yasgui.closeTab(tab);
+```
+
+##### `getTabs(): { [tabId: string]: Tab }`
+
+Get all tabs.
+
+```javascript
+const allTabs = yasgui.getTabs();
+Object.keys(allTabs).forEach(id => {
+ console.log('Tab:', allTabs[id].getName());
+});
+```
+
+##### `setTheme(theme: 'light' | 'dark'): void`
+
+Set the theme.
+
+```javascript
+yasgui.setTheme('dark');
+```
+
+##### `getTheme(): 'light' | 'dark'`
+
+Get the current theme.
+
+```javascript
+const theme = yasgui.getTheme();
+console.log('Current theme:', theme);
+```
+
+##### `toggleTheme(): 'light' | 'dark'`
+
+Toggle between themes and return the new theme.
+
+```javascript
+const newTheme = yasgui.toggleTheme();
+console.log('Switched to:', newTheme);
+```
+
+### Tab Class
+
+Represents a query tab.
+
+#### Methods
+
+##### `getName(): string`
+
+Get tab name.
+
+```javascript
+const name = tab.getName();
+```
+
+##### `setName(name: string): void`
+
+Set tab name.
+
+```javascript
+tab.setName('My Query Tab');
+```
+
+##### `getId(): string`
+
+Get tab ID.
+
+```javascript
+const id = tab.getId();
+```
+
+##### `getYasqe(): Yasqe`
+
+Get YASQE instance for this tab.
+
+```javascript
+const yasqe = tab.getYasqe();
+yasqe.setValue('SELECT * WHERE { ?s ?p ?o }');
+```
+
+##### `getYasr(): Yasr`
+
+Get YASR instance for this tab.
+
+```javascript
+const yasr = tab.getYasr();
+yasr.draw();
+```
+
+##### `query(): Promise`
+
+Execute the query in this tab.
+
+```javascript
+tab.query()
+ .then(() => console.log('Query completed'))
+ .catch(err => console.error('Query failed:', err));
+```
+
+##### `setQuery(query: string): void`
+
+Set the query value.
+
+```javascript
+tab.setQuery('SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10');
+```
+
+##### `getQuery(): string`
+
+Get the current query.
+
+```javascript
+const query = tab.getQuery();
+console.log('Current query:', query);
+```
+
+### Yasqe Class
+
+SPARQL query editor.
+
+#### Methods
+
+##### `getValue(): string`
+
+Get the query text.
+
+```javascript
+const query = yasqe.getValue();
+```
+
+##### `setValue(value: string): void`
+
+Set the query text.
+
+```javascript
+yasqe.setValue('SELECT * WHERE { ?s ?p ?o } LIMIT 10');
+```
+
+##### `query(): Promise`
+
+Execute the query.
+
+```javascript
+yasqe.query()
+ .then(response => console.log('Results:', response))
+ .catch(err => console.error('Error:', err));
+```
+
+##### `abortQuery(): void`
+
+Abort the current query.
+
+```javascript
+yasqe.abortQuery();
+```
+
+##### `format(): void`
+
+Format the query using the selected formatter.
+
+```javascript
+yasqe.format();
+```
+
+##### `getPrefixes(): Prefixes`
+
+Get defined prefixes in the query.
+
+```javascript
+const prefixes = yasqe.getPrefixes();
+console.log('Prefixes:', prefixes);
+```
+
+##### `addPrefixes(prefixes: Prefixes): void`
+
+Add prefixes to the query.
+
+```javascript
+yasqe.addPrefixes({
+ foaf: 'http://xmlns.com/foaf/0.1/',
+ dc: 'http://purl.org/dc/elements/1.1/'
+});
+```
+
+##### `removePrefixes(): void`
+
+Remove all PREFIX declarations from the query.
+
+```javascript
+yasqe.removePrefixes();
+```
+
+### Yasr Class
+
+SPARQL results viewer.
+
+#### Methods
+
+##### `setResponse(response: any, duration?: number): void`
+
+Set the query response to display.
+
+```javascript
+yasr.setResponse(response, 1234); // duration in ms
+```
+
+##### `draw(): void`
+
+Redraw the current plugin.
+
+```javascript
+yasr.draw();
+```
+
+##### `selectPlugin(pluginName: string): void`
+
+Select a specific plugin.
+
+```javascript
+yasr.selectPlugin('table');
+```
+
+##### `getPlugins(): { [name: string]: Plugin }`
+
+Get all registered plugins.
+
+```javascript
+const plugins = yasr.getPlugins();
+console.log('Available plugins:', Object.keys(plugins));
+```
+
+##### `download(filename?: string): void`
+
+Download results using current plugin's download method.
+
+```javascript
+yasr.download('my-results.csv');
+```
+
+---
+
+## Events
+
+YASGUI uses an event-driven architecture. All components extend EventEmitter and emit events for various actions.
+
+### YASGUI Events
+
+Listen to events on the main YASGUI instance:
+
+```javascript
+const yasgui = new Yasgui(element);
+
+// Tab events
+yasgui.on('tabSelect', (instance, tabId) => {
+ console.log('Tab selected:', tabId);
+});
+
+yasgui.on('tabAdd', (instance, tabId) => {
+ console.log('Tab added:', tabId);
+});
+
+yasgui.on('tabClose', (instance, tab) => {
+ console.log('Tab closed:', tab.getName());
+});
+
+yasgui.on('tabChange', (instance, tab) => {
+ console.log('Tab changed:', tab.getName());
+});
+
+yasgui.on('tabOrderChanged', (instance, tabList) => {
+ console.log('Tab order changed:', tabList);
+});
+
+// Query events
+yasgui.on('query', (instance, tab) => {
+ console.log('Query started on tab:', tab.getName());
+});
+
+yasgui.on('queryBefore', (instance, tab) => {
+ console.log('Query about to start on tab:', tab.getName());
+});
+
+yasgui.on('queryResponse', (instance, tab) => {
+ console.log('Query completed on tab:', tab.getName());
+});
+
+yasgui.on('queryAbort', (instance, tab) => {
+ console.log('Query aborted on tab:', tab.getName());
+});
+
+// Fullscreen events
+yasgui.on('fullscreen-enter', (instance) => {
+ console.log('Entered fullscreen');
+});
+
+yasgui.on('fullscreen-leave', (instance) => {
+ console.log('Exited fullscreen');
+});
+
+// Autocomplete events
+yasgui.on('autocompletionShown', (instance, tab, widget) => {
+ console.log('Autocomplete shown on tab:', tab.getName());
+});
+
+yasgui.on('autocompletionClose', (instance, tab) => {
+ console.log('Autocomplete closed on tab:', tab.getName());
+});
+
+// Endpoint events
+yasgui.on('endpointHistoryChange', (instance, history) => {
+ console.log('Endpoint history updated:', history);
+});
+```
+
+### YASQE Events
+
+Listen to events on YASQE instances:
+
+```javascript
+const yasqe = new Yasqe(element);
+
+yasqe.on('query', (instance, request, abortController) => {
+ console.log('Query started with endpoint:', request.url);
+});
+
+yasqe.on('queryBefore', (instance, config) => {
+ console.log('Query about to start with config:', config);
+ // Modify config here if needed
+});
+
+yasqe.on('queryResponse', (instance, response, duration) => {
+ console.log('Query completed in', duration, 'ms');
+});
+
+yasqe.on('queryResults', (instance, results, duration) => {
+ console.log('Results received:', results);
+});
+
+yasqe.on('queryAbort', (instance, request) => {
+ console.log('Query aborted');
+});
+
+yasqe.on('error', (instance) => {
+ console.log('Error occurred');
+});
+
+yasqe.on('blur', (instance) => {
+ console.log('Editor lost focus');
+});
+
+yasqe.on('autocompletionShown', (instance, widget) => {
+ console.log('Autocomplete widget shown');
+});
+
+yasqe.on('autocompletionClose', (instance) => {
+ console.log('Autocomplete closed');
+});
+
+yasqe.on('resize', (instance, newSize) => {
+ console.log('Editor resized to:', newSize);
+});
+
+// CodeMirror events (yasqe extends CodeMirror)
+yasqe.on('change', (instance, changeObj) => {
+ console.log('Editor content changed');
+});
+
+yasqe.on('cursorActivity', (instance) => {
+ console.log('Cursor moved');
+});
+```
+
+### YASR Events
+
+Listen to events on YASR instances:
+
+```javascript
+const yasr = new Yasr(element);
+
+yasr.on('change', (instance) => {
+ console.log('YASR state changed');
+});
+
+yasr.on('draw', (instance, plugin) => {
+ console.log('Started drawing with plugin:', plugin.label);
+});
+
+yasr.on('drawn', (instance, plugin) => {
+ console.log('Finished drawing with plugin:', plugin.label);
+});
+
+yasr.on('toggle-help', (instance) => {
+ console.log('Help toggled');
+});
+```
+
+### Event Example: Query Tracking
+
+Track query execution time and results:
+
+```javascript
+const yasgui = new Yasgui(element);
+
+yasgui.on('queryBefore', (instance, tab) => {
+ tab.queryStartTime = Date.now();
+ console.log('Query started:', tab.getQuery());
+});
+
+yasgui.on('queryResponse', (instance, tab) => {
+ const duration = Date.now() - tab.queryStartTime;
+ console.log('Query completed in', duration, 'ms');
+
+ const yasr = tab.getYasr();
+ if (yasr.results) {
+ console.log('Result type:', yasr.results.getType());
+ if (yasr.results.getType() === 'json') {
+ const bindings = yasr.results.getBindings();
+ console.log('Result count:', bindings?.length || 0);
+ }
+ }
+});
+
+yasgui.on('queryAbort', (instance, tab) => {
+ console.log('Query aborted after', Date.now() - tab.queryStartTime, 'ms');
+});
+```
+
+### Event Example: Custom Query Logging
+
+Log all queries to a backend service:
+
+```javascript
+yasgui.on('query', async (instance, tab) => {
+ const logData = {
+ query: tab.getQuery(),
+ endpoint: tab.getYasqe().config.requestConfig.endpoint,
+ timestamp: new Date().toISOString(),
+ tabName: tab.getName()
+ };
+
+ try {
+ await fetch('/api/log-query', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(logData)
+ });
+ } catch (err) {
+ console.error('Failed to log query:', err);
+ }
+});
+```
+
+---
+
+## Plugin Development
+
+YASR uses a plugin system for result visualization. You can create custom plugins to visualize results in new ways.
+
+### Plugin Interface
+
+A YASR plugin must implement the `Plugin` interface:
+
+```typescript
+interface Plugin {
+ // Priority for automatic plugin selection (higher = preferred)
+ priority: number;
+
+ // Label shown in plugin selector
+ label?: string;
+
+ // Plugin-specific options
+ options?: Options;
+
+ // Hide plugin from manual selection
+ hideFromSelection?: boolean;
+
+ // Help/documentation URL
+ helpReference?: string;
+
+ // Determine if this plugin can handle current results
+ canHandleResults(): boolean;
+
+ // Initialize plugin (async setup)
+ initialize?(): Promise;
+
+ // Cleanup when plugin destroyed
+ destroy?(): void;
+
+ // Draw visualization
+ draw(persistentConfig: any, runtimeConfig?: any): Promise | void;
+
+ // Return plugin icon for selector
+ getIcon(): Element | undefined;
+
+ // Provide download functionality
+ download?(filename?: string): DownloadInfo | undefined;
+}
+
+interface DownloadInfo {
+ contentType: string;
+ getData: () => string;
+ filename: string;
+ title: string;
+}
+```
+
+### Step-by-Step Plugin Development Guide
+
+#### Step 1: Create Plugin Class
+
+Create a class implementing the Plugin interface:
+
+```typescript
+import { Plugin } from '@matdata/yasr';
+
+export default class MyCustomPlugin implements Plugin {
+ private yasr: Yasr;
+ private container: HTMLElement;
+
+ // Priority for auto-selection (higher = more likely to be auto-selected)
+ public priority = 5;
+
+ // Label shown in UI
+ public label = 'My Visualization';
+
+ // Help URL
+ public helpReference = 'https://example.com/help';
+
+ constructor(yasr: Yasr) {
+ this.yasr = yasr;
+ this.container = document.createElement('div');
+ this.container.className = 'my-custom-plugin';
+ yasr.resultsEl.appendChild(this.container);
+ }
+
+ // Determine if this plugin can visualize current results
+ canHandleResults(): boolean {
+ // Check if results exist and are of the right type
+ if (!this.yasr.results) return false;
+
+ // Example: only handle SELECT query results
+ const results = this.yasr.results;
+ if (results.getType() === 'json') {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Return icon for plugin selector
+ getIcon(): Element | undefined {
+ const icon = document.createElement('span');
+ icon.textContent = 'π'; // Or use SVG
+ return icon;
+ }
+
+ // Draw the visualization
+ async draw(persistentConfig: any, runtimeConfig?: any): Promise {
+ if (!this.yasr.results) return;
+
+ // Clear container
+ this.container.innerHTML = '';
+
+ // Get results
+ const bindings = this.yasr.results.getBindings();
+ if (!bindings) return;
+
+ // Create your visualization
+ const table = document.createElement('table');
+ table.className = 'my-custom-table';
+
+ // Add headers
+ const thead = document.createElement('thead');
+ const headerRow = document.createElement('tr');
+ const vars = this.yasr.results.getVariables();
+ vars?.forEach(varName => {
+ const th = document.createElement('th');
+ th.textContent = varName;
+ headerRow.appendChild(th);
+ });
+ thead.appendChild(headerRow);
+ table.appendChild(thead);
+
+ // Add data rows
+ const tbody = document.createElement('tbody');
+ bindings.forEach(binding => {
+ const row = document.createElement('tr');
+ vars?.forEach(varName => {
+ const td = document.createElement('td');
+ const value = binding[varName];
+ td.textContent = value?.value || '';
+ row.appendChild(td);
+ });
+ tbody.appendChild(row);
+ });
+ table.appendChild(tbody);
+
+ this.container.appendChild(table);
+ }
+
+ // Optional: Provide download functionality
+ download(filename?: string): DownloadInfo | undefined {
+ if (!this.yasr.results) return undefined;
+
+ // Generate CSV
+ const bindings = this.yasr.results.getBindings();
+ const vars = this.yasr.results.getVariables();
+
+ let csv = vars?.join(',') + '\n';
+ bindings?.forEach(binding => {
+ const row = vars?.map(v => {
+ const val = binding[v]?.value || '';
+ return `"${val.replace(/"/g, '""')}"`;
+ }).join(',');
+ csv += row + '\n';
+ });
+
+ return {
+ contentType: 'text/csv',
+ getData: () => csv,
+ filename: filename || 'results.csv',
+ title: 'Download as CSV'
+ };
+ }
+
+ // Optional: Cleanup
+ destroy(): void {
+ this.container.remove();
+ }
+}
+```
+
+#### Step 2: Register Plugin
+
+Register your plugin with YASR:
+
+```javascript
+import Yasr from '@matdata/yasr';
+import MyCustomPlugin from './MyCustomPlugin';
+
+// Register globally
+Yasr.registerPlugin('MyCustomPlugin', MyCustomPlugin);
+
+// Now use YASR as normal
+const yasr = new Yasr(element);
+```
+
+Or register for a specific YASR instance:
+
+```javascript
+const yasr = new Yasr(element);
+yasr.registerPlugin('MyCustomPlugin', MyCustomPlugin);
+```
+
+#### Step 3: Configure Plugin
+
+Configure plugin-specific options:
+
+```javascript
+const yasr = new Yasr(element, {
+ plugins: {
+ MyCustomPlugin: {
+ priority: 10,
+ // Custom plugin options
+ customOption: 'value'
+ }
+ }
+});
+```
+
+#### Step 4: Add Styling
+
+Create a CSS file for your plugin:
+
+```css
+.my-custom-plugin {
+ padding: 20px;
+ background: var(--yasgui-bg-primary);
+ color: var(--yasgui-text-primary);
+}
+
+.my-custom-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.my-custom-table th,
+.my-custom-table td {
+ padding: 8px;
+ border: 1px solid var(--yasgui-border-color);
+ text-align: left;
+}
+
+.my-custom-table th {
+ background: var(--yasgui-bg-secondary);
+ font-weight: bold;
+}
+
+/* Dark theme support */
+[data-theme="dark"] .my-custom-plugin {
+ background: var(--yasgui-bg-primary);
+}
+```
+
+### Plugin Example: Chart Plugin
+
+Complete example of a chart visualization plugin:
+
+```typescript
+import { Plugin } from '@matdata/yasr';
+import Chart from 'chart.js/auto';
+
+export default class ChartPlugin implements Plugin {
+ private yasr: Yasr;
+ private container: HTMLElement;
+ private canvas: HTMLCanvasElement;
+ private chart: Chart | null = null;
+
+ public priority = 7;
+ public label = 'Chart';
+ public helpReference = 'https://example.com/chart-help';
+
+ constructor(yasr: Yasr) {
+ this.yasr = yasr;
+ this.container = document.createElement('div');
+ this.container.className = 'yasr-chart-plugin';
+ this.canvas = document.createElement('canvas');
+ this.container.appendChild(this.canvas);
+ yasr.resultsEl.appendChild(this.container);
+ }
+
+ canHandleResults(): boolean {
+ if (!this.yasr.results) return false;
+
+ // Only handle SELECT results with numeric values
+ const results = this.yasr.results;
+ if (results.getType() !== 'json') return false;
+
+ const vars = results.getVariables();
+ const bindings = results.getBindings();
+
+ // Need at least 2 variables (label and value)
+ if (!vars || vars.length < 2 || !bindings || bindings.length === 0) {
+ return false;
+ }
+
+ // Check if second variable contains numbers
+ const firstBinding = bindings[0];
+ const valueVar = vars[1];
+ const value = firstBinding[valueVar]?.value;
+
+ return !isNaN(parseFloat(value));
+ }
+
+ getIcon(): Element | undefined {
+ const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ icon.setAttribute('viewBox', '0 0 24 24');
+ icon.setAttribute('width', '24');
+ icon.setAttribute('height', '24');
+
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+ path.setAttribute('d', 'M3 13h2v8H3zm4-4h2v12H7zm4-4h2v16h-2zm4 2h2v14h-2zm4 4h2v10h-2z');
+ path.setAttribute('fill', 'currentColor');
+
+ icon.appendChild(path);
+ return icon;
+ }
+
+ async draw(persistentConfig: any, runtimeConfig?: any): Promise {
+ if (!this.yasr.results) return;
+
+ const results = this.yasr.results;
+ const vars = results.getVariables();
+ const bindings = results.getBindings();
+
+ if (!vars || !bindings) return;
+
+ // Extract data
+ const labels: string[] = [];
+ const data: number[] = [];
+
+ bindings.forEach(binding => {
+ const label = binding[vars[0]]?.value || '';
+ const value = parseFloat(binding[vars[1]]?.value || '0');
+ labels.push(label);
+ data.push(value);
+ });
+
+ // Destroy previous chart
+ if (this.chart) {
+ this.chart.destroy();
+ }
+
+ // Create chart
+ const ctx = this.canvas.getContext('2d');
+ if (!ctx) return;
+
+ this.chart = new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: labels,
+ datasets: [{
+ label: vars[1],
+ data: data,
+ backgroundColor: 'rgba(75, 192, 192, 0.6)',
+ borderColor: 'rgba(75, 192, 192, 1)',
+ borderWidth: 1
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ }
+
+ destroy(): void {
+ if (this.chart) {
+ this.chart.destroy();
+ }
+ this.container.remove();
+ }
+}
+```
+
+### Plugin Best Practices
+
+1. **Theme Support**: Use CSS custom properties for colors
+2. **Responsive**: Make visualizations responsive to container size
+3. **Error Handling**: Gracefully handle invalid or missing data
+4. **Performance**: Optimize for large result sets
+5. **Accessibility**: Include ARIA labels and keyboard navigation
+6. **Documentation**: Provide clear documentation and examples
+7. **TypeScript**: Use TypeScript for better type safety
+8. **Testing**: Write unit tests for your plugin
+
+### Distributing Your Plugin
+
+Package and distribute your plugin:
+
+```json
+{
+ "name": "yasr-my-plugin",
+ "version": "1.0.0",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "peerDependencies": {
+ "@matdata/yasr": "^4.0.0"
+ }
+}
+```
+
+Users can install and use it:
+
+```bash
+npm install yasr-my-plugin
+```
+
+```javascript
+import Yasr from '@matdata/yasr';
+import MyPlugin from 'yasr-my-plugin';
+
+Yasr.registerPlugin('MyPlugin', MyPlugin);
+```
+
+---
+
+## Contributing
+
+We welcome contributions to YASGUI! Here's how to get involved.
+
+### Getting Started
+
+1. **Fork the Repository**
+ ```bash
+ git clone https://github.com/Matdata-eu/Yasgui.git
+ cd Yasgui
+ ```
+
+2. **Install Dependencies**
+ ```bash
+ npm install
+ ```
+
+3. **Run Development Server**
+ ```bash
+ npm run dev
+ ```
+
+ Visit `http://localhost:5173/demo` to see YASGUI in action.
+
+4. **Make Changes**
+ - Create a feature branch: `git checkout -b feature/my-feature`
+ - Make your changes
+ - Test locally
+
+5. **Build**
+ ```bash
+ npm run build
+ ```
+
+6. **Run Tests**
+ ```bash
+ npm test
+ ```
+
+7. **Run Linter**
+ ```bash
+ npm run util:lint
+ ```
+
+### Project Structure
+
+```
+Yasgui/
+βββ packages/
+β βββ utils/ # Shared utilities
+β βββ yasqe/ # Query editor
+β βββ yasr/ # Results viewer
+β βββ yasgui/ # Main package
+βββ dev/ # Development pages
+βββ build/ # Build output
+βββ docs/ # Documentation
+βββ .github/ # GitHub workflows
+βββ test/ # Tests
+```
+
+### Development Workflow
+
+#### Making Changes
+
+1. **Code Style**: Follow the existing code style
+ - Use TypeScript
+ - Follow ESLint rules (`npm run util:lint`)
+ - Use Prettier for formatting (`npm run util:prettify`)
+
+2. **Commit Messages**: Use conventional commit format
+ ```
+ feat: add new plugin system
+ fix: resolve query execution bug
+ docs: update API documentation
+ style: format code
+ refactor: restructure plugin loading
+ test: add unit tests for parser
+ chore: update dependencies
+ ```
+
+3. **Testing**: Add tests for new features
+ ```bash
+ npm run unit-test # Unit tests
+ npm run puppeteer-test # E2E tests
+ ```
+
+#### Building
+
+```bash
+npm run build # Production build
+npm run dev # Development server
+```
+
+#### Pull Requests
+
+1. **Create PR** with clear description
+2. **Link Issues**: Reference related issues
+3. **Tests**: Ensure all tests pass
+4. **Documentation**: Update docs if needed
+5. **Changelog**: Add entry if applicable
+
+### Code Guidelines
+
+#### TypeScript
+
+- Use strict TypeScript settings
+- Define interfaces for all public APIs
+- Avoid `any` types when possible
+- Export types for library consumers
+
+```typescript
+// Good
+interface PluginConfig {
+ priority: number;
+ enabled: boolean;
+}
+
+function configurePlugin(config: PluginConfig): void {
+ // Implementation
+}
+
+// Avoid
+function configurePlugin(config: any) {
+ // Implementation
+}
+```
+
+#### CSS
+
+- Use CSS custom properties for theming
+- Follow BEM naming convention
+- Support both light and dark themes
+
+```css
+/* Good */
+.yasgui__header {
+ background: var(--yasgui-bg-primary);
+ color: var(--yasgui-text-primary);
+}
+
+.yasgui__header--collapsed {
+ height: 0;
+}
+
+/* Avoid inline styles or theme-specific colors */
+```
+
+#### Documentation
+
+- Document all public APIs
+- Include code examples
+- Keep README up to date
+- Add JSDoc comments
+
+```typescript
+/**
+ * Execute a SPARQL query against the configured endpoint.
+ *
+ * @param query - The SPARQL query string
+ * @param options - Optional request configuration
+ * @returns Promise resolving to query results
+ * @throws {Error} If query is invalid or network request fails
+ *
+ * @example
+ * ```typescript
+ * const results = await yasqe.query(
+ * 'SELECT * WHERE { ?s ?p ?o } LIMIT 10',
+ * { endpoint: 'https://dbpedia.org/sparql' }
+ * );
+ * ```
+ */
+async query(query: string, options?: RequestConfig): Promise {
+ // Implementation
+}
+```
+
+### Reporting Issues
+
+When reporting bugs:
+
+1. **Search Existing Issues**: Check if already reported
+2. **Provide Details**:
+ - YASGUI version
+ - Browser and version
+ - Steps to reproduce
+ - Expected vs actual behavior
+ - Error messages
+ - Example query (if applicable)
+
+3. **Use Issue Template**: Follow the provided template
+
+### Feature Requests
+
+When requesting features:
+
+1. **Describe Use Case**: Explain the problem
+2. **Propose Solution**: Suggest implementation
+3. **Consider Alternatives**: Mention other approaches
+4. **Provide Examples**: Show how it would work
+
+### Release Process
+
+Releases are managed using Changesets:
+
+1. **Create Changeset**
+ ```bash
+ npx changeset
+ ```
+
+2. **Select Packages**: Choose affected packages
+3. **Describe Changes**: Write changelog entry
+4. **Commit**: Commit changeset file
+
+5. **Release** (maintainers only)
+ ```bash
+ npm run release
+ ```
+
+### Community
+
+- **GitHub Discussions**: Ask questions, share ideas
+- **Issues**: Report bugs, request features
+- **Pull Requests**: Contribute code
+
+### Code of Conduct
+
+- Be respectful and inclusive
+- Welcome newcomers
+- Focus on constructive feedback
+- Follow GitHub's Community Guidelines
+
+---
+
+## Additional Resources
+
+- **GitHub Repository**: [https://github.com/Matdata-eu/Yasgui](https://github.com/Matdata-eu/Yasgui)
+- **Issue Tracker**: [https://github.com/Matdata-eu/Yasgui/issues](https://github.com/Matdata-eu/Yasgui/issues)
+- **User Guide**: See `docs/user-guide.md`
+- **SPARQL Specification**: [https://www.w3.org/TR/sparql11-query/](https://www.w3.org/TR/sparql11-query/)
+- **CodeMirror Documentation**: [https://codemirror.net/5/](https://codemirror.net/5/)
+- **Graph Plugin**: [https://github.com/Matdata-eu/yasgui-graph-plugin](https://github.com/Matdata-eu/yasgui-graph-plugin)
+- **Geo Plugin**: [https://github.com/Thib-G/yasgui-geo-tg](https://github.com/Thib-G/yasgui-geo-tg)
+
+---
+
+*This developer guide is maintained as part of the YASGUI project. For user-facing documentation, see the User Guide.*
diff --git a/docs/user-guide.md b/docs/user-guide.md
new file mode 100644
index 00000000..015c1a62
--- /dev/null
+++ b/docs/user-guide.md
@@ -0,0 +1,753 @@
+# YASGUI User Guide
+
+**Yet Another SPARQL GUI (YASGUI)** is a powerful, user-friendly interface for querying and exploring RDF data using SPARQL. This guide will help you understand and make the most of YASGUI's features.
+
+## Table of Contents
+
+1. [Introduction](#introduction)
+2. [What is SPARQL?](#what-is-sparql)
+3. [Getting Started](#getting-started)
+4. [Components Overview](#components-overview)
+5. [Features](#features)
+6. [Plugins](#plugins)
+7. [Keyboard Shortcuts](#keyboard-shortcuts)
+8. [Troubleshooting](#troubleshooting)
+
+---
+
+## Introduction
+
+YASGUI is a comprehensive SPARQL query interface that combines a powerful query editor (YASQE) with a versatile results viewer (YASR). Whether you're new to SPARQL or an experienced user, YASGUI provides an intuitive interface for working with semantic data.
+
+**Key Benefits:**
+- Write and execute SPARQL queries against any endpoint
+- Visualize results in multiple formats (tables, graphs, maps, etc.)
+- Save and manage multiple query tabs
+- Auto-complete and syntax highlighting
+- Theme support (light and dark modes)
+- Responsive layouts for different screen sizes
+
+---
+
+## What is SPARQL?
+
+**SPARQL** (SPARQL Protocol and RDF Query Language) is the standard query language for retrieving and manipulating data stored in RDF (Resource Description Framework) format. Think of it as SQL for linked data and knowledge graphs.
+
+### Basic Concepts
+
+- **Triples**: RDF data is organized as subject-predicate-object statements
+- **URIs**: Resources are identified using URIs (Uniform Resource Identifiers)
+- **Prefixes**: Shortcuts for long URIs (e.g., `foaf:` for `http://xmlns.com/foaf/0.1/`)
+- **Endpoints**: Web services that accept SPARQL queries
+
+### Learning SPARQL
+
+For a comprehensive, beginner-friendly guide to SPARQL, visit:
+**[https://kvistgaard.github.io/sparql/](https://kvistgaard.github.io/sparql/)**
+
+This excellent resource covers:
+- SPARQL syntax and structure
+- Query types (SELECT, CONSTRUCT, ASK, DESCRIBE)
+- Filters and functions
+- Real-world examples and exercises
+
+### Common SPARQL Query Types
+
+- **SELECT**: Returns a table of results (like SQL SELECT)
+- **CONSTRUCT**: Creates new RDF triples from query results
+- **ASK**: Returns true/false for whether a pattern exists
+- **DESCRIBE**: Returns all information about a resource
+
+---
+
+## Getting Started
+
+### Accessing YASGUI
+
+YASGUI is available at: **[https://yasgui.matdata.eu/](https://yasgui.matdata.eu/)**
+
+### Your First Query
+
+1. **Select an Endpoint**: Enter a SPARQL endpoint URL in the endpoint field (or use the default)
+2. **Write a Query**: Type your SPARQL query in the editor
+3. **Execute**: Click the "Execute" button or press `Ctrl+Enter`
+4. **View Results**: Results appear in the results viewer below
+
+**Example Query:**
+```sparql
+SELECT ?subject ?predicate ?object
+WHERE {
+ ?subject ?predicate ?object
+}
+LIMIT 10
+```
+
+### Saving Your Work
+
+YASGUI automatically saves:
+- Your queries in local storage
+- Tab configurations
+- User preferences (theme, layout, etc.)
+
+Your work persists across browser sessions on the same device.
+
+---
+
+## Components Overview
+
+YASGUI consists of three main components working together:
+
+### YASQE (Query Editor)
+
+The query editor where you write and edit SPARQL queries.
+
+**Features:**
+- Syntax highlighting for SPARQL
+- Auto-completion for keywords, prefixes, and URIs
+- Line numbers and code folding
+- Query validation with error highlighting
+- Find and replace functionality
+- Multiple editor themes
+
+### YASR (Results Viewer)
+
+The results viewer displays query results in various formats.
+
+**Features:**
+- Multiple visualization plugins (table, graph, map, etc.)
+- Export results to different formats
+- Pagination for large result sets
+- Sortable and filterable tables
+- Raw response viewer
+
+### YASGUI (Main Interface)
+
+The container that brings YASQE and YASR together with additional functionality.
+
+**Features:**
+- Multiple query tabs
+- Tab management (rename, duplicate, close)
+- Endpoint management
+- Settings configuration
+- Theme switching
+- Layout orientation options
+
+---
+
+## Features
+
+### Themes
+
+Switch between light and dark modes to match your preference or environment.
+
+**How to Change Theme:**
+- Click the theme toggle button (βοΈ/π) in the top-right area of the tab bar
+- Theme preference is automatically saved
+
+**Theme Characteristics:**
+- **Light Theme**: Bright, high-contrast interface ideal for well-lit environments
+- **Dark Theme**: Easy-on-the-eyes interface perfect for low-light conditions or extended use
+
+Both themes include:
+- Consistent styling across all components
+- Syntax highlighting optimized for readability
+- Themed modal dialogs and controls
+
+### Layout Orientation
+
+YASGUI offers two layout options to optimize screen space:
+
+**Vertical Layout (Default):**
+- Query editor on top
+- Results viewer below
+- Best for standard monitors and laptops
+
+**Horizontal Layout:**
+- Query editor on left
+- Results viewer on right (side-by-side)
+- Ideal for wide/ultrawide monitors
+- Maximizes vertical space for both components
+
+**How to Change Layout:**
+- Click the layout toggle button in the control bar (next to the endpoint selector)
+- Icon shows stacked rectangles (vertical) or side-by-side rectangles (horizontal)
+- Layout preference is saved automatically
+
+### Query Formatting
+
+Keep your SPARQL queries clean and readable with automatic formatting.
+
+**Features:**
+- Format button in the editor toolbar
+- Keyboard shortcut: `Shift+Ctrl+F`
+- Choice of formatting engines:
+ - **sparql-formatter** (default): Standards-compliant, modern formatter
+ - **Legacy formatter**: Original YASGUI formatter
+- Auto-format on query execution (configurable in Settings)
+
+**How to Format:**
+1. Write or paste your query
+2. Click the format button or press `Shift+Ctrl+F`
+3. Query is automatically reformatted with proper indentation and spacing
+
+**Configuration:**
+- Open Settings (β icon) to:
+ - Select your preferred formatter
+ - Enable/disable auto-format on query execution
+ - Both settings are saved persistently
+
+### Fullscreen Mode
+
+Maximize screen space for editing or viewing results.
+
+**Available Modes:**
+- **YASQE Fullscreen**: Editor takes up the entire screen
+- **YASR Fullscreen**: Results viewer takes up the entire screen
+
+**How to Use:**
+- Press `F11` to toggle YASQE fullscreen
+- Press `F10` to toggle YASR fullscreen
+- Press `F9` to switch between YASQE and YASR fullscreen
+- Press `Esc` or click the close button to exit fullscreen
+
+### Prefix Management
+
+Simplify query writing with reusable prefix declarations.
+
+**Features:**
+- **Saved Prefixes**: Define commonly-used prefixes once, reuse everywhere
+- **Auto-capture**: YASGUI automatically captures new prefixes from your queries
+- **PREFIX Button**: Insert saved prefixes into your query with one click
+- **Prefix Autocomplete**: Type a prefix (e.g., `PREFIX foaf:`) and YASGUI suggests the full URI from prefix.cc
+
+**Default Prefixes:**
+YASGUI includes standard prefixes like:
+- `rdf:` - RDF vocabulary
+- `rdfs:` - RDF Schema
+
+**How to Use:**
+1. Click the "PREFIX" button to insert all saved prefixes
+2. Open Settings (β) to manage your prefix collection
+3. Enable/disable auto-capture in Settings
+
+**Auto-completion:**
+When typing a prefix declaration, YASGUI queries [prefix.cc](https://prefix.cc) to suggest standard URIs:
+```sparql
+PREFIX foaf: < # Auto-suggests http://xmlns.com/foaf/0.1/
+```
+
+### Endpoint Quick Switch
+
+Quickly switch between different SPARQL endpoints with predefined buttons.
+
+**Features:**
+- One-click endpoint switching
+- Predefined endpoint buttons (configured by administrators)
+- Custom user-defined endpoints (saved in local storage)
+- Fully themed buttons
+
+**How to Add Custom Endpoints:**
+1. Click Settings (β) in the control bar
+2. Navigate to "Endpoint Buttons" tab
+3. Enter label and endpoint URL
+4. Click "+ Add Button"
+5. Click "Save"
+
+Your custom endpoints appear alongside predefined buttons and persist across sessions.
+
+**How to Switch Endpoints:**
+- Click any endpoint button to immediately update the endpoint field
+- The new endpoint is used for subsequent queries
+
+### URI Explorer
+
+Quickly explore RDF resources by Ctrl+clicking on URIs in your query.
+
+**How It Works:**
+1. Hold `Ctrl` and click any URI in the query editor
+2. YASGUI automatically generates and executes a CONSTRUCT query exploring:
+ - Outgoing triples (where the URI is the subject)
+ - Incoming triples (where the URI is the object)
+3. Results appear without modifying your original query
+4. View the resource's connections in the results viewer
+
+**Example:**
+Ctrl+clicking on `http://dbpedia.org/resource/Amsterdam` automatically queries for all triples related to Amsterdam.
+
+### Query Tabs
+
+Manage multiple queries simultaneously with tabs.
+
+**Features:**
+- Create unlimited query tabs
+- Rename tabs by double-clicking the tab name
+- Duplicate tabs to create query variations
+- Reorder tabs by dragging
+- Close individual tabs
+- Each tab maintains its own:
+ - Query content
+ - Endpoint configuration
+ - Results
+ - Plugin selection
+
+**Tab Operations:**
+- **New Tab**: Click the "+" button in the tab bar
+- **Rename**: Double-click the tab name
+- **Duplicate**: Right-click and select "Duplicate"
+- **Close**: Click the "Γ" on the tab or use the context menu
+- **Reorder**: Drag and drop tabs to rearrange
+
+### Settings Modal
+
+Access comprehensive configuration options through the Settings modal.
+
+**How to Open:**
+- Click the settings button (β) in the control bar
+
+**Settings Sections:**
+
+**Prefixes Tab:**
+- Manage saved prefix declarations
+- Enable/disable auto-capture
+- Add, edit, or remove prefixes
+
+**Request Configuration Tab:**
+- HTTP request method (GET/POST)
+- Accept headers
+- Custom request parameters
+- Named/default graphs configuration
+- Custom HTTP headers
+
+**Endpoint Buttons Tab:**
+- Add custom endpoint quick-switch buttons
+- Remove user-defined buttons
+- Manage endpoint collection
+
+**Formatting Tab:**
+- Select formatter (sparql-formatter or legacy)
+- Enable/disable auto-format on query execution
+
+All settings are saved automatically to local storage.
+
+### Query History and Persistence
+
+YASGUI automatically saves your work locally.
+
+**What's Saved:**
+- Query text in each tab
+- Tab names and order
+- Endpoint configurations
+- Last query results
+- User preferences (theme, layout, etc.)
+- Custom endpoint buttons
+
+**Persistence Duration:**
+- Default: 30 days
+- Configurable by administrators
+
+**Note:** Data is stored in your browser's local storage. Clearing browser data will remove saved queries.
+
+### Share Queries
+
+Share your queries with colleagues using URL parameters.
+
+**How to Share:**
+YASGUI supports URL-based query sharing:
+1. Craft your query in YASGUI
+2. The URL updates to include query parameters
+3. Share the URL with others
+4. Recipients see your query pre-loaded when they visit the link
+
+**URL Parameters:**
+- `query`: The SPARQL query
+- `endpoint`: The endpoint URL
+- Other tab configurations
+
+---
+
+## Plugins
+
+YASR (Results Viewer) uses plugins to visualize query results in different formats. The appropriate plugin is automatically selected based on your query type and results.
+
+### Table Plugin
+
+Displays SELECT query results in an interactive table.
+
+**Features:**
+- **Sortable Columns**: Click column headers to sort
+- **Pagination**: Navigate large result sets efficiently
+- **Column Resizing**: Drag column borders to resize
+- **Filtering**: Filter rows by column values
+- **Cell Formatting**: URIs are clickable links, literals shown with datatypes
+- **Export**: Download results as CSV
+
+**Best For:**
+- SELECT queries
+- Exploring structured data
+- Comparing values across multiple rows
+
+**Usage:**
+- Execute a SELECT query
+- Table plugin activates automatically
+- Click column headers to sort
+- Use pagination controls at the bottom
+- Export via the download button
+
+### Boolean Plugin
+
+Displays boolean results from ASK queries.
+
+**Features:**
+- Clear true/false visual indicator
+- Color-coded responses (green for true, red for false)
+- Large, easy-to-read display
+
+**Best For:**
+- ASK queries
+- Checking if patterns exist in the data
+- Boolean validation queries
+
+**Usage:**
+- Execute an ASK query
+- Boolean plugin activates automatically
+- Result shown as TRUE β or FALSE β
+
+### Response Plugin
+
+Shows the raw response from the SPARQL endpoint.
+
+**Features:**
+- Syntax highlighting for different formats:
+ - JSON
+ - XML
+ - Turtle
+ - Other RDF serializations
+- Code folding for nested structures
+- Copy to clipboard functionality
+- Line numbers
+
+**Best For:**
+- Debugging queries
+- Inspecting raw endpoint responses
+- Understanding response structure
+- Working with specific response formats
+
+**Usage:**
+- Execute any query
+- Select "Response" from the plugin selector
+- View raw response with syntax highlighting
+
+### Graph Plugin
+
+Visualizes RDF data as an interactive node-edge graph.
+
+**Features:**
+- Interactive force-directed graph layout
+- Zoom and pan navigation
+- Node and edge highlighting on hover
+- Clickable nodes to explore connections
+- Theme support (light/dark)
+- Export graph visualization
+
+**Best For:**
+- CONSTRUCT queries
+- DESCRIBE queries
+- Exploring RDF structure and relationships
+- Visualizing knowledge graphs
+
+**Usage:**
+- Execute a CONSTRUCT or DESCRIBE query
+- Select "Graph" from the plugin selector
+- Interact with nodes by clicking and dragging
+- Zoom with mouse wheel
+- Pan by dragging the background
+
+**Configuration:**
+Graph appearance is automatically themed to match YASGUI's current theme.
+
+**More Information:**
+Visit the [Graph Plugin Repository](https://github.com/Matdata-eu/yasgui-graph-plugin) for detailed documentation.
+
+### Geo Plugin
+
+Displays geographic data on an interactive map.
+
+**Features:**
+- Interactive map with zoom and pan
+- Marker clustering for dense data
+- Popup information on marker click
+- Multiple map layer options
+- Theme-aware styling
+
+**Best For:**
+- Queries returning coordinates (latitude/longitude)
+- Geographic/spatial data
+- Location-based queries
+- Visualizing geographic distributions
+
+**Usage:**
+- Execute a query returning geo-coordinates
+- Results should include latitude/longitude properties
+- Select "Geo" from the plugin selector
+- Interact with map markers
+
+**Expected Data Format:**
+The plugin looks for common coordinate properties:
+- `wkt` (Well-Known Text)
+- `lat`/`long` or `latitude`/`longitude`
+- GeoJSON structures
+
+**More Information:**
+Visit [Yasgui Geo Plugin](https://github.com/Thib-G/yasgui-geo-tg) for detailed documentation.
+
+### Error Plugin
+
+Displays error messages and diagnostic information when queries fail.
+
+**Features:**
+- Detailed error messages
+- HTTP status codes
+- SPARQL endpoint errors
+- CORS troubleshooting guidance
+- Helpful suggestions for common issues
+
+**Best For:**
+- Understanding query failures
+- Debugging endpoint issues
+- Troubleshooting CORS problems
+
+**When It Appears:**
+- Query execution fails
+- Network errors occur
+- SPARQL syntax errors
+- Endpoint unavailable
+
+**Common Error Types:**
+- **CORS Errors**: Cross-origin request blocked
+- **Syntax Errors**: Invalid SPARQL syntax
+- **Timeout**: Query took too long
+- **404**: Endpoint not found
+- **500**: Server error
+
+### Plugin Selection
+
+**Automatic Selection:**
+YASGUI automatically selects the most appropriate plugin based on:
+- Query type (SELECT, CONSTRUCT, ASK, DESCRIBE)
+- Response content type
+- Data structure
+
+**Manual Selection:**
+- Use the plugin selector buttons at the top of the results area
+- Available plugins depend on the query results
+- Your selection is saved per tab
+
+---
+
+## Keyboard Shortcuts
+
+Master YASGUI with these keyboard shortcuts for faster querying.
+
+### Query Editor (YASQE)
+
+| Shortcut | Action |
+|----------|--------|
+| `Ctrl+Enter` / `Cmd+Enter` | Execute query |
+| `Ctrl+Space` | Trigger autocomplete |
+| `Ctrl+S` | Save query to local storage |
+| `Shift+Ctrl+F` | Format query |
+| `Ctrl+/` | Toggle comment on selected lines |
+| `Shift+Ctrl+D` | Duplicate current line |
+| `Shift+Ctrl+K` | Delete current line |
+| `Esc` | Remove focus from editor |
+| `Ctrl+Click` (on URI) | Explore URI connections |
+
+### Fullscreen
+
+| Shortcut | Action |
+|----------|--------|
+| `F11` | Toggle YASQE (editor) fullscreen |
+| `F10` | Toggle YASR (results) fullscreen |
+| `F9` | Switch between YASQE and YASR fullscreen |
+| `Esc` | Exit fullscreen mode |
+
+### General Editor
+
+| Shortcut | Action |
+|----------|--------|
+| `Ctrl+F` / `Cmd+F` | Find in query |
+| `Ctrl+H` / `Cmd+H` | Find and replace |
+| `Ctrl+G` / `Cmd+G` | Go to line |
+| `Ctrl+Z` / `Cmd+Z` | Undo |
+| `Ctrl+Y` / `Cmd+Y` | Redo |
+| `Tab` | Indent selection |
+| `Shift+Tab` | Outdent selection |
+
+### Results Viewer (YASR)
+
+Navigation and interaction depend on the active plugin. Most plugins support:
+- **Mouse wheel**: Scroll through results
+- **Arrow keys**: Navigate table cells (in Table plugin)
+- **Click**: Select/interact with elements
+
+---
+
+## Troubleshooting
+
+### Common Issues and Solutions
+
+#### Query Not Executing
+
+**Symptoms:**
+- Click Execute but nothing happens
+- No results appear
+
+**Solutions:**
+1. **Check Query Syntax**: Look for red error indicators in the editor
+2. **Verify Endpoint**: Ensure the endpoint URL is correct and accessible
+3. **Check Browser Console**: Open browser developer tools (F12) for error messages
+4. **Check Internet Connection**: Ensure you're online
+5. **Try a Simple Query**: Execute a basic query to test the endpoint:
+ ```sparql
+ SELECT * WHERE { ?s ?p ?o } LIMIT 1
+ ```
+
+#### CORS Errors
+
+**Symptoms:**
+- Error message about "Cross-Origin Request Blocked"
+- Query works in other tools but not in YASGUI
+
+**Explanation:**
+CORS (Cross-Origin Resource Sharing) is a browser security feature. Some SPARQL endpoints don't allow browser-based queries from different domains.
+
+**Solutions:**
+1. **Check Endpoint CORS Policy**: Contact the endpoint administrator
+2. **Use CORS Proxy**: Configure a CORS proxy in YASGUI settings (if provided by administrator)
+3. **Use Desktop/Server Version**: Run YASGUI locally or server-side where CORS doesn't apply
+4. **Server-Side Proxy**: Query through a backend service
+
+#### Slow Queries
+
+**Symptoms:**
+- Query takes very long to execute
+- Browser becomes unresponsive
+
+**Solutions:**
+1. **Add LIMIT Clause**: Restrict result size:
+ ```sparql
+ SELECT * WHERE { ?s ?p ?o } LIMIT 100
+ ```
+2. **Optimize Query**: Make query more specific with filters
+3. **Check Endpoint Status**: Endpoint might be under heavy load
+4. **Break Into Smaller Queries**: Split complex queries into simpler parts
+5. **Use Query Timeout**: Set a reasonable timeout in settings
+
+#### Results Not Displaying Correctly
+
+**Symptoms:**
+- Results appear but look wrong
+- Visualization doesn't show expected data
+
+**Solutions:**
+1. **Try Different Plugin**: Switch between Table, Response, and other plugins
+2. **Check Response Plugin**: View raw response to verify data structure
+3. **Check Data Format**: Ensure query returns expected format for the plugin
+4. **Clear Cache**: Clear browser cache and reload YASGUI
+5. **Check Browser Compatibility**: Use a modern browser (Chrome, Firefox, Safari, Edge)
+
+#### Autocomplete Not Working
+
+**Symptoms:**
+- No suggestions appear when typing
+- Ctrl+Space does nothing
+
+**Solutions:**
+1. **Check Internet Connection**: Prefix suggestions require network access
+2. **Wait for Typing Pause**: Autocomplete appears after a brief delay
+3. **Position Cursor Correctly**: Autocomplete context-dependent (prefixes, keywords, etc.)
+4. **Manually Trigger**: Press `Ctrl+Space` to force autocomplete
+
+#### Lost Queries
+
+**Symptoms:**
+- Previous queries disappeared
+- Tabs missing after reload
+
+**Solutions:**
+1. **Check Browser Storage**: Ensure browser allows local storage
+2. **Avoid Private/Incognito Mode**: These modes don't persist local storage
+3. **Check Storage Limits**: Browser might have cleared old data
+4. **Export Important Queries**: Save critical queries externally
+5. **Use Bookmarks**: Bookmark queries as URLs for safekeeping
+
+#### Theme/Layout Not Saving
+
+**Symptoms:**
+- Theme reverts to default on reload
+- Layout orientation doesn't persist
+
+**Solutions:**
+1. **Enable Local Storage**: Check browser settings
+2. **Check Private Mode**: Use normal browser mode
+3. **Clear and Reset**: Clear browser data, reconfigure preferences
+4. **Check Browser Compatibility**: Ensure modern browser with localStorage support
+
+#### Formatting Issues
+
+**Symptoms:**
+- Format button doesn't work
+- Query becomes malformed after formatting
+
+**Solutions:**
+1. **Check Query Syntax**: Formatter requires valid SPARQL
+2. **Try Different Formatter**: Switch between sparql-formatter and legacy in Settings
+3. **Manual Formatting**: Format query manually if auto-format fails
+4. **Report Invalid Results**: If formatter produces incorrect output, it's a bug
+
+### Getting Help
+
+If you encounter issues not covered here:
+
+1. **Check GitHub Issues**: Visit [YASGUI Issues](https://github.com/Matdata-eu/Yasgui/issues)
+2. **Search Existing Issues**: Your problem might already be reported
+3. **Create New Issue**: Provide:
+ - YASGUI version
+ - Browser and version
+ - Steps to reproduce
+ - Error messages
+ - Example query (if applicable)
+4. **Community Support**: Engage with the YASGUI community
+
+### Best Practices
+
+**For Reliable Querying:**
+- Always use `LIMIT` when exploring new data
+- Test complex queries incrementally
+- Save important queries outside the browser
+- Use meaningful tab names
+- Keep YASGUI updated (if self-hosted)
+- Monitor endpoint status pages
+- Use appropriate query types (SELECT vs CONSTRUCT)
+
+**For Better Performance:**
+- Format queries for readability
+- Use specific property paths instead of `?p`
+- Add filters early in query patterns
+- Avoid `SELECT *` on large datasets
+- Close unused tabs
+- Clear old results periodically
+
+---
+
+## Additional Resources
+
+- **SPARQL Tutorial**: [https://kvistgaard.github.io/sparql/](https://kvistgaard.github.io/sparql/)
+- **YASGUI GitHub**: [https://github.com/Matdata-eu/Yasgui](https://github.com/Matdata-eu/Yasgui)
+- **Developer Documentation**: See Developer Guide for API and integration details
+- **RDF Primer**: [https://www.w3.org/TR/rdf11-primer/](https://www.w3.org/TR/rdf11-primer/)
+- **SPARQL Specification**: [https://www.w3.org/TR/sparql11-query/](https://www.w3.org/TR/sparql11-query/)
+
+---
+
+*This user guide is maintained as part of the YASGUI project. For technical implementation details, see the Developer Guide.*
From 10c27d913992735cd75acd19132cbf3433925ed3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 09:50:48 +0000
Subject: [PATCH 3/6] feat: setup Docusaurus for documentation with GitHub
Pages deployment
Co-authored-by: MathiasVDA <15101339+MathiasVDA@users.noreply.github.com>
---
.github/workflows/deploy-docs.yml | 87 +
.github/workflows/deploy-pages.yml | 58 -
.gitignore | 6 +-
README.md | 28 +-
website/.gitignore | 20 +
website/README.md | 41 +
website/blog/2019-05-28-first-blog-post.md | 12 +
website/blog/2019-05-29-long-blog-post.md | 44 +
website/blog/2021-08-01-mdx-blog-post.mdx | 24 +
.../docusaurus-plushie-banner.jpeg | Bin 0 -> 96122 bytes
website/blog/2021-08-26-welcome/index.md | 29 +
website/blog/authors.yml | 25 +
website/blog/tags.yml | 19 +
website/docs/developer-guide.md | 2019 ++
website/docs/intro.md | 79 +
website/docs/user-guide.md | 757 +
website/docusaurus.config.ts | 147 +
website/package-lock.json | 18146 ++++++++++++++++
website/package.json | 47 +
website/sidebars.ts | 23 +
.../src/components/HomepageFeatures/index.tsx | 71 +
.../HomepageFeatures/styles.module.css | 11 +
website/src/css/custom.css | 30 +
website/src/pages/index.module.css | 23 +
website/src/pages/index.tsx | 44 +
website/src/pages/markdown-page.md | 7 +
website/static/.nojekyll | 0
website/static/img/docusaurus-social-card.jpg | Bin 0 -> 55746 bytes
website/static/img/docusaurus.png | Bin 0 -> 5142 bytes
website/static/img/favicon.ico | Bin 0 -> 3626 bytes
website/static/img/logo.svg | 1 +
.../static/img/undraw_docusaurus_mountain.svg | 171 +
.../static/img/undraw_docusaurus_react.svg | 170 +
website/static/img/undraw_docusaurus_tree.svg | 40 +
website/tsconfig.json | 8 +
35 files changed, 22124 insertions(+), 63 deletions(-)
create mode 100644 .github/workflows/deploy-docs.yml
delete mode 100644 .github/workflows/deploy-pages.yml
create mode 100644 website/.gitignore
create mode 100644 website/README.md
create mode 100644 website/blog/2019-05-28-first-blog-post.md
create mode 100644 website/blog/2019-05-29-long-blog-post.md
create mode 100644 website/blog/2021-08-01-mdx-blog-post.mdx
create mode 100644 website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg
create mode 100644 website/blog/2021-08-26-welcome/index.md
create mode 100644 website/blog/authors.yml
create mode 100644 website/blog/tags.yml
create mode 100644 website/docs/developer-guide.md
create mode 100644 website/docs/intro.md
create mode 100644 website/docs/user-guide.md
create mode 100644 website/docusaurus.config.ts
create mode 100644 website/package-lock.json
create mode 100644 website/package.json
create mode 100644 website/sidebars.ts
create mode 100644 website/src/components/HomepageFeatures/index.tsx
create mode 100644 website/src/components/HomepageFeatures/styles.module.css
create mode 100644 website/src/css/custom.css
create mode 100644 website/src/pages/index.module.css
create mode 100644 website/src/pages/index.tsx
create mode 100644 website/src/pages/markdown-page.md
create mode 100644 website/static/.nojekyll
create mode 100644 website/static/img/docusaurus-social-card.jpg
create mode 100644 website/static/img/docusaurus.png
create mode 100644 website/static/img/favicon.ico
create mode 100644 website/static/img/logo.svg
create mode 100644 website/static/img/undraw_docusaurus_mountain.svg
create mode 100644 website/static/img/undraw_docusaurus_react.svg
create mode 100644 website/static/img/undraw_docusaurus_tree.svg
create mode 100644 website/tsconfig.json
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
new file mode 100644
index 00000000..cdc5a423
--- /dev/null
+++ b/.github/workflows/deploy-docs.yml
@@ -0,0 +1,87 @@
+name: Deploy Documentation to GitHub Pages
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ branches:
+ - main
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: "lts/*"
+ cache: npm
+ cache-dependency-path: |
+ package-lock.json
+ website/package-lock.json
+
+ - name: Disable AppArmor
+ run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
+
+ # Build YASGUI application
+ - name: Install YASGUI dependencies
+ run: npm ci
+
+ - name: Build YASGUI
+ run: npm run build
+ env:
+ PUBLIC_PATH: /Yasgui/dev/main/
+
+ # Build Docusaurus documentation
+ - name: Install Docusaurus dependencies
+ working-directory: ./website
+ run: npm ci
+
+ - name: Build Docusaurus
+ working-directory: ./website
+ run: npm run build
+
+ # Prepare combined deployment
+ - name: Prepare deployment directory
+ run: |
+ mkdir -p deployment
+ # Copy Docusaurus documentation to root
+ cp -r website/build/* deployment/
+ # Copy YASGUI build to /dev/main subdirectory
+ mkdir -p deployment/dev/main
+ cp -r build/* deployment/dev/main/
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./deployment
+
+ deploy:
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ needs: build
+ runs-on: ubuntu-latest
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml
deleted file mode 100644
index 6e23b087..00000000
--- a/.github/workflows/deploy-pages.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-name: Deploy to GitHub Pages
-
-on:
- push:
- branches:
- - main
- pull_request:
- branches:
- - main
-
-permissions:
- contents: read
- pages: write
- id-token: write
-
-concurrency:
- group: "pages"
- cancel-in-progress: false
-
-jobs:
- deploy:
- runs-on: ubuntu-latest
- environment:
- name: github-pages
- url: ${{ steps.deployment.outputs.page_url }}
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v5
-
- - name: Set up Node.js
- uses: actions/setup-node@v6
- with:
- node-version: "lts/*"
- cache: npm
-
- - name: Disable AppArmor
- run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
-
- - name: Install dependencies
- run: npm ci
-
- - name: Build project
- run: npm run build
- env:
- PUBLIC_PATH: /Yasgui/
-
- - name: Setup Pages
- uses: actions/configure-pages@v4
-
- - name: Upload artifact
- uses: actions/upload-pages-artifact@v3
- with:
- path: ./build
-
- - name: Deploy to GitHub Pages
- id: deployment
- uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
index 1bde0a30..0871f44e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,8 @@ node_modules
build
yarn.lock
.vscode
-build.old
\ No newline at end of file
+build.old
+# Docusaurus
+website/node_modules
+website/build
+website/.docusaurus
diff --git a/README.md b/README.md
index 543f940a..936bd884 100644
--- a/README.md
+++ b/README.md
@@ -23,12 +23,32 @@
## Documentation
-The **documentation for YASGUI is now hosted on GitHub Pages**:
+The **documentation for YASGUI is hosted on GitHub Pages**:
-- **Documentation Website**: [https://matdata-eu.github.io/Yasgui/](https://matdata-eu.github.io/Yasgui/) (coming soon with Docusaurus)
-- **Development Environment**: [https://matdata-eu.github.io/Yasgui/dev/main/](https://matdata-eu.github.io/Yasgui/dev/main/) - Live build from the main branch
+- **π Documentation Website**: [https://matdata-eu.github.io/Yasgui/](https://matdata-eu.github.io/Yasgui/)
+ - User Guide, Developer Guide, API Reference
+ - Built with Docusaurus
+ - Version-tagged with the repository
-The documentation is version-tagged with the repository, ensuring consistency between code and docs.
+- **π Development Build**: [https://matdata-eu.github.io/Yasgui/dev/main/](https://matdata-eu.github.io/Yasgui/dev/main/)
+ - Live build from the main branch
+ - Updated automatically with every commit
+ - Test latest features before release
+
+### GitHub Pages Structure
+
+```
+https://matdata-eu.github.io/Yasgui/
+βββ / # Documentation (Docusaurus)
+β βββ docs/
+β β βββ user-guide
+β β βββ developer-guide
+β βββ ...
+βββ /dev/main/ # Development build of YASGUI app
+ βββ index.html
+```
+
+The documentation is version-tagged with the repository, ensuring consistency between code and documentation across releases.
## Features
diff --git a/website/.gitignore b/website/.gitignore
new file mode 100644
index 00000000..b2d6de30
--- /dev/null
+++ b/website/.gitignore
@@ -0,0 +1,20 @@
+# Dependencies
+/node_modules
+
+# Production
+/build
+
+# Generated files
+.docusaurus
+.cache-loader
+
+# Misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
diff --git a/website/README.md b/website/README.md
new file mode 100644
index 00000000..b28211a9
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1,41 @@
+# Website
+
+This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
+
+## Installation
+
+```bash
+yarn
+```
+
+## Local Development
+
+```bash
+yarn start
+```
+
+This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
+
+## Build
+
+```bash
+yarn build
+```
+
+This command generates static content into the `build` directory and can be served using any static contents hosting service.
+
+## Deployment
+
+Using SSH:
+
+```bash
+USE_SSH=true yarn deploy
+```
+
+Not using SSH:
+
+```bash
+GIT_USER= yarn deploy
+```
+
+If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
diff --git a/website/blog/2019-05-28-first-blog-post.md b/website/blog/2019-05-28-first-blog-post.md
new file mode 100644
index 00000000..d3032efb
--- /dev/null
+++ b/website/blog/2019-05-28-first-blog-post.md
@@ -0,0 +1,12 @@
+---
+slug: first-blog-post
+title: First Blog Post
+authors: [slorber, yangshun]
+tags: [hola, docusaurus]
+---
+
+Lorem ipsum dolor sit amet...
+
+
+
+...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
diff --git a/website/blog/2019-05-29-long-blog-post.md b/website/blog/2019-05-29-long-blog-post.md
new file mode 100644
index 00000000..eb4435de
--- /dev/null
+++ b/website/blog/2019-05-29-long-blog-post.md
@@ -0,0 +1,44 @@
+---
+slug: long-blog-post
+title: Long Blog Post
+authors: yangshun
+tags: [hello, docusaurus]
+---
+
+This is the summary of a very long blog post,
+
+Use a `` comment to limit blog post size in the list view.
+
+
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
diff --git a/website/blog/2021-08-01-mdx-blog-post.mdx b/website/blog/2021-08-01-mdx-blog-post.mdx
new file mode 100644
index 00000000..0c4b4a48
--- /dev/null
+++ b/website/blog/2021-08-01-mdx-blog-post.mdx
@@ -0,0 +1,24 @@
+---
+slug: mdx-blog-post
+title: MDX Blog Post
+authors: [slorber]
+tags: [docusaurus]
+---
+
+Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
+
+:::tip
+
+Use the power of React to create interactive blog posts.
+
+:::
+
+{/* truncate */}
+
+For example, use JSX to create an interactive button:
+
+```js
+
+```
+
+
diff --git a/website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/website/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..11bda0928456b12f8e53d0ba5709212a4058d449
GIT binary patch
literal 96122
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