diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 9da6f70..5f71b83 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -29,14 +29,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Use Node.js
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
node-version: '18'
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Pages
id: pages
- uses: actions/configure-pages@v3
+ uses: actions/configure-pages@v5
- name: Build
env:
# For maximum backward compatibility with Hugo modules
@@ -46,7 +46,7 @@ jobs:
npm install
npm run build
- name: Upload artifact
- uses: actions/upload-pages-artifact@v3
+ uses: actions/upload-pages-artifact@v3.0.1
with:
path: ./public
diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml
index b13e442..8a13645 100644
--- a/.github/workflows/preview.yml
+++ b/.github/workflows/preview.yml
@@ -20,11 +20,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Use Node.js
- uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2
+ uses: actions/setup-node@v4
with:
node-version: '18'
- name: Checkout
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
+ uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Build
@@ -32,7 +32,7 @@ jobs:
npm install
npm run build
- name: Upload artifact
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+ uses: actions/upload-artifact@v4
with:
name: 'site'
path: ./public
@@ -49,13 +49,13 @@ jobs:
# checkout required for pr-preview-action to succeed,
# while the content will not be used
- name: Checkout
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
+ uses: actions/checkout@v4
- name: Download the preview page
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+ uses: actions/download-artifact@v4
with:
name: 'site'
path: ./public
- - uses: rossjrw/pr-preview-action@4668d7cb417ce7067b0b59bc152b1ae1513010de # v1.4.6
+ - uses: rossjrw/pr-preview-action@v1
id: deployment
with:
source-dir: ./public
@@ -75,8 +75,8 @@ jobs:
# checkout required for pr-preview-action to succeed,
# while the content will not be used
- name: Checkout
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- - uses: rossjrw/pr-preview-action@4668d7cb417ce7067b0b59bc152b1ae1513010de # v1.4.6
+ uses: actions/checkout@v4
+ - uses: rossjrw/pr-preview-action@v1
id: deployment
with:
preview-branch: previews
diff --git a/hugo/content/docs/concepts/architecture-overview.md b/hugo/content/docs/concepts/architecture-overview.md
new file mode 100644
index 0000000..bb39b7e
--- /dev/null
+++ b/hugo/content/docs/concepts/architecture-overview.md
@@ -0,0 +1,49 @@
+---
+title: 'Architecture Overview'
+---
+
+{{< toc >}}
+
+Sprotty is a diagramming framework that provides a robust foundation for building interactive, web-based diagram editors. At its core, Sprotty follows a clear architectural pattern that ensures maintainability, testability, and extensibility.
+
+## Introduction to Sprotty's Architecture
+
+The foundation of Sprotty's architecture is built around three main components that work together in a unidirectional flow:
+
+1. **Action Dispatcher**: The central hub that receives and processes all diagram operations
+2. **Command Stack**: The state manager that maintains the diagram's history and current state
+3. **Viewer**: The renderer that transforms the internal model into a visual representation
+
+These components form a cyclic flow of information that prevents feedback loops and ensures predictable behavior.
+
+{{< mermaid class="text-center">}}
+flowchart TD;
+ ActionDispatcher[Action Dispatcher]
+ CommandStack[Command Stack]
+ Viewer[Viewer]
+ ActionDispatcher -->|Command| CommandStack
+ CommandStack -->|SModel| Viewer
+ Viewer -->|Action| ActionDispatcher
+{{< /mermaid>}}
+
+This architecture provides several key benefits:
+
+- **Predictable State Management**: The unidirectional flow makes it easy to track and debug state changes
+- **Testability**: Each component has a clear responsibility and can be tested in isolation
+- **Extensibility**: New features can be added by extending any of the core components
+- **Performance**: The architecture minimizes unnecessary updates and optimizes rendering
+
+## What You'll Learn
+
+This documentation series will guide you through Sprotty's architecture in logical steps:
+
+1. **[Core Components]({{< relref "core-components" >}})**: Deep dive into the three main architectural components
+2. **[Data Flow]({{< relref "data-flow" >}})**: Understanding how information moves through the system
+3. **[Extension Points]({{< relref "extension-points" >}})**: How to customize and extend Sprotty's functionality
+4. **[Best Practices]({{< relref "best-practices" >}})**: Guidelines for building robust Sprotty applications
+
+Each section builds upon the previous ones, providing a comprehensive understanding of how to build effective diagramming applications with Sprotty.
+
+## Next Steps
+
+Start with the [Core Components]({{< relref "core-components" >}}) section to understand the fundamental building blocks of Sprotty's architecture, then progress through each section to build a complete understanding of the framework.
diff --git a/hugo/content/docs/concepts/best-practices.md b/hugo/content/docs/concepts/best-practices.md
new file mode 100644
index 0000000..4efc665
--- /dev/null
+++ b/hugo/content/docs/concepts/best-practices.md
@@ -0,0 +1,398 @@
+---
+title: 'Best Practices'
+weight: 5
+---
+
+{{< toc >}}
+
+Now that you understand Sprotty's [Extension Points]({{< relref "extension-points" >}}), let's explore the best practices for building robust, maintainable, and performant diagramming applications. These guidelines will help you avoid common pitfalls and create high-quality Sprotty-based solutions.
+
+## Architecture & Design Patterns
+
+### Dependency Injection (DI) Configuration
+
+- **Use InversifyJS properly:** Always use `@injectable` decorators for your classes and configure them in DI modules
+- **Use utility functions for your modules:** Use utility functions like `configureModelElement()`, `configureCommand()`, `configureActionHandler()`, etc. to configure your DI modules
+- **Load modules in the correct order:** Start with `loadDefaultModules()` then add your custom modules
+
+**✅ Always use proper DI configuration:**
+
+```typescript
+// Good: Proper DI setup with type mapping
+const customModule = new ContainerModule((bind, unbind, isBound, rebind) => {
+ // Map model types to implementations and views
+ configureModelElement(context, 'task', RectangularNode, TaskNodeView);
+ configureModelElement(context, 'edge', SEdgeImpl, PolylineEdgeView);
+
+ // Bind services with proper scoping
+ bind(TYPES.ModelSource).to(CustomModelSource).inSingletonScope();
+ bind(TYPES.IActionDispatcher).to(ActionDispatcher).inSingletonScope();
+
+ // Configure viewer options
+ configureViewerOptions(context, {
+ needsClientLayout: true,
+ baseDiv: 'sprotty-diagram'
+ });
+});
+
+// Load modules in correct order
+const container = new Container();
+loadDefaultModules(container);
+container.load(customModule);
+```
+
+**❌ Avoid direct instantiation:**
+
+```typescript
+// Bad: Direct instantiation makes testing difficult
+private actionDispatcher = new ActionDispatcher();
+private modelSource = new LocalModelSource();
+```
+
+### Model Design Principles
+
+- **Extend existing interfaces:** Create custom types by extending Sprotty's base interfaces (SNode, SEdge, etc.)
+- **Use JSON-serializable models:** Ensure your model elements can be serialized for client-server communication
+- **Implement proper ID management:** Use unique, consistent IDs for all elements
+- **Reserve system properties:** Don't override reserved properties like children, parent, index
+
+**✅ Extend existing interfaces properly:**
+
+```typescript
+// Good: Extend base interfaces for custom types
+export interface TaskNode extends SNode {
+ name: string;
+ isRunning: boolean;
+ isFinished: boolean;
+ priority?: 'low' | 'medium' | 'high';
+}
+
+// Good: Use JSON-serializable models
+export const taskModel: SGraph = {
+ type: 'graph',
+ id: 'task-graph',
+ children: [
+ {
+ type: 'task',
+ id: 'task-1',
+ name: 'First Task',
+ isRunning: false,
+ isFinished: true,
+ position: { x: 0, y: 0 },
+ size: { width: 100, height: 50 }
+ }
+ ]
+};
+```
+
+**❌ Don't override reserved properties:**
+
+```typescript
+// Bad: Overriding reserved properties
+const node = {
+ type: 'task',
+ id: 'task-1',
+ children: 'invalid', // Reserved property
+ parent: 'invalid', // Reserved property
+ index: 'invalid' // Reserved property
+};
+```
+
+## Performance & Scalability
+
+### Large Diagram Optimization
+
+- **Use view filtering:** Implement conditional rendering for large datasets
+- **Leverage projection features:** Use projection bars for navigation in large diagrams
+- **Implement lazy loading:** Load and/or display diagram sections on demand
+- **Optimize bounds computation:** Provide valid bounds in your model to avoid expensive calculations
+
+**✅ Implement conditional rendering for large datasets:**
+
+```typescript
+// Good: Use ThunkView for performance optimization
+@injectable()
+export class OptimizedNodeView extends ThunkView {
+ watchedArgs(model: SNode): any[] {
+ return [model.isVisible, model.zoom, model.position];
+ }
+
+ selector(model: SNode): string {
+ return 'g';
+ }
+
+ isVisible(model: SNode): boolean {
+ // Only render when zoomed in enough
+ return model.zoom * model.size.width > 10;
+ }
+
+ render(model: SNode, context: RenderingContext): VNode {
+ return