Format: Keep a Changelog.
instanceId is now REQUIRED everywhere (no auto-generation fallback).
initializeStructuraWebview({ instanceId: 'unique-id', ... })—instanceIdis mandatorycreateSchemaBuilder({ instanceId: 'unique-id', ... })—instanceIdis mandatory (in props object, not optional)createCanvasPage(instanceId, config?, mcpUrl?)—instanceIdis now a required first positional parametercreate_redux_store(config, instanceId)—instanceIdis required (throws if missing)getInstanceReduxStore(instanceId, config)— Use this instead of deprecatedgetGlobalReduxStore(now throws)initToolboxConfigManager(instanceId)—instanceIdis required (throws if missing)initExportRegistry(instanceId)—instanceIdis required (throws if missing)createPersistence(store, instanceId)—instanceIdis required (throws if missing)
Type system enforces instanceId: Attempting to omit instanceId now results in TypeScript compilation errors.
Rationale: The v1.2.x fallback to auto-generation created accidental safety issues where developers could deploy instances without proper isolation. Requiring explicit instanceId ensures all multi-instance scenarios are explicit and verifiable.
- [CRITICAL] LocalStorage State Collision in Shared Origins — When multiple Structura instances run in the same browser origin (e.g., multiple VS Code webview panels in a single extension), they were colliding in localStorage due to hardcoded keys. v2.0.0 enforces this fix as mandatory:
- All storage keys are now strictly namespaced:
${instanceId}:vbe2:*,${instanceId}:vbs-*,${instanceId}:atomos_* getGlobalReduxStore()removed entirely (deprecated function now throws)- All persistence functions throw if
instanceIdis missing or empty - No fallback behavior — every instance must have an explicit, non-empty
instanceId - See Bug Report: LocalStorage State Collision below.
- All storage keys are now strictly namespaced:
-
schema-create-autointent action — The schema tab bar now dispatches a lightweightschema-create-autoaction instead of generating an ID and firingschema-createddirectly. In standalone mode the Redux reducer handles it. When an MCP server URL is configured the canvas page intercepts the action via a dispatch hook, forwards the request to the MCP server, and the SSEschema-createdevent closes the loop. This eliminates duplicate ID generation between the UI and the server. (create-schema-tabs.ts,create-redux-store.ts,create-canvas-page.ts) -
addDispatchHookonReduxStore— A lightweight middleware hook that lets consumers intercept Redux actions before they reach the reducer. Returningnullfrom a hook swallows the action; returning the action (or a replacement) forwards it. Used internally to routeschema-create-autoto MCP. (redux-state.types.ts,create-redux-store.ts) -
Canvas adapter registry —
getCanvasAdapterFor(schemaId)replaces the global singleton canvas adapter. Each schema ID maps to its ownCanvasAdapterinstance, preventing cross-schema state pollution when multiple panels are open simultaneously. The oldgetCanvasAdapter()is kept as deprecated. (canvas-adapter.ts) -
Explicit
schema_idin MCP entity/link tools —create-entity,update-entity,delete-entity,create-linknow require aschema_idparameter and return400when it is absent or404when the schema is not found.get-entityfalls back to the active schema for backwards compatibility. ThechangeSSE event payload now includesschema_id. (mcp-server.ts) -
Template injection tokens —
webview/template.htmlnow ships with${mcpUrl}and${schemaId}tokens instead of a commented-out placeholder. Consuming extensions call.replaceAll('${mcpUrl}', url)and.replaceAll('${schemaId}', id)insidebuildHtml. Empty strings are treated asundefinedby the webview init, preserving standalone-mode behaviour. (template.html,webview/index.ts) -
Single-file IIFE build —
pnpm run build:webview-iife(envBUILD_TARGET=webview-iife) producesdist/webview/index.iife.js— a self-contained bundle with no dynamic imports and the global nameStructuraWebview. Recommended for VSIX-packaged extensions where ES module dynamic imports are unreliable. (vite.config.ts,package.json) -
__APP_VERSION__build constant —vite.config.tsinjects__APP_VERSION__frompackage.jsonat build time. Used by the new About modal. (vite.config.ts) -
About modal — New
createAboutModal()utility renders the package name, version (from__APP_VERSION__), MIT licence notice, and links to documentation and the repository. Accessible via the toolbar burger menu ("About" button with info icon). (create-about-modal.ts,create-canvas-toolbar.ts) -
--atp-modal-paddingCSS custom property — Theatp-modalshadow DOM.dialognow accepts--atp-modal-padding(default0 4px) so consuming applications can adjust internal padding without replacing the entire style. (atp-modal.style.ts)
-
create-mcp-sync.ts—applyChangenow readsschema_idfrom the SSE payload and applies the update to that specific schema rather than always targeting the active schema. (create-mcp-sync.ts) -
Entity settings modal button style — Removed hardcoded
height: 24px; min-height: 24pxoverride onaddPropBtn; size is now determined by padding alone, matching other toolbar buttons. (create-entity-settings-modal.ts)
-
EXTENSION_GUIDE.md— UpdatedbuildHtmlsample to use token replacements; added "Instance Isolation" and "Single-bundle IIFE" sections. (EXTENSION_GUIDE.md) -
structura-mcp/README.md— Added "Targeting a specific schema" section withschema_idusage examples and SSE routing pattern. (README.md) -
docs/IMPLEMENTATION_STATUS.md— Added architectural change note block documenting the v1.2.0 isolation improvements. (IMPLEMENTATION_STATUS.md)
- Initial production release of
@atomos-web/structura,@atomos-web/structura-core,@atomos-web/structura-mcp. - 20+ MCP tools for AI-driven schema creation.
- Viewport control, session lifecycle, availability guards, menu configuration.
- Export/import (JSON workspace, SVG, PNG).
- Redux undo/redo with history skip for volatile actions.
@atomos-web/primeweb component library:atp-modal,atp-dropdown, design system tokens.
Structura relied on hardcoded localStorage keys for persistence. When multiple instances run in the same browser origin, all instances share the exact same localStorage environment, causing a singleton-like bug where canvases overwrite each other's state.
Affected Storage Keys:
| File | Keys | Status |
|---|---|---|
create-redux-store.ts |
vbe2:redux-state |
Fixed v1.2.2 |
create-persistence.ts |
vbs-canvas-state |
Fixed v1.2.2 |
toolbox-config-manager.ts |
atomos_toolbox_config, atomos_custom_shapes, atomos_general_settings, atomos_appearance_settings |
Fixed v1.2.2 |
create-export-registry.ts |
vbe2:custom-export-plugins |
Fixed v1.2.2 |
schema-builder.ts |
close() method clears all vbe2:* keys |
Fixed v1.2.2 |
// Webview Panel 1 opens "Schema A"
const app1 = await initializeStructuraWebview({
containerId: 'app1',
instanceId: 'panel-1' // REQUIRED in v1.2.2+
});
// Webview Panel 2 opens "Schema B" — same origin
const app2 = await initializeStructuraWebview({
containerId: 'app2',
instanceId: 'panel-2' // REQUIRED in v1.2.2+
});
// BEFORE FIX: Both stored state in same keys
// localStorage['vbe2:redux-state'] → continuously overwritten
// localStorage['vbs-canvas-state'] → continuously overwritten
// localStorage['atomos_toolbox_config'] → shared settings
// AFTER FIX: Fully namespaced
// localStorage['panel-1:vbe2:redux-state']
// localStorage['panel-2:vbe2:redux-state']
// localStorage['panel-1:vbs-canvas-state']
// localStorage['panel-2:vbs-canvas-state']
// localStorage['panel-1:atomos_toolbox_config']
// localStorage['panel-2:atomos_toolbox_config']All persistence layers now follow this pattern:
// Before
const STORAGE_KEY = 'some-key';
localStorage.setItem(STORAGE_KEY, value);
// After
export const someFunction = function(config: SomeConfig, instanceId?: string) {
const STORAGE_KEY = `${instanceId}:some-key`;
localStorage.setItem(STORAGE_KEY, value);
}If you're using the headless API:
// v1.2.x (optional instanceId)
const builder = createSchemaBuilder();
// v2.0.0+ (REQUIRED instanceId)
const builder = createSchemaBuilder({
instanceId: 'my-app-instance-1' // MANDATORY — no auto-generation
});If you're embedding in VS Code extensions:
// v1.2.x (optional instanceId, auto-generates if missing)
const app = await initializeStructuraWebview({
containerId: 'app',
// instanceId omitted — falls back to UUID
mcpServerUrl: '...'
});
// v2.0.0+ (REQUIRED instanceId)
const app = await initializeStructuraWebview({
containerId: 'app',
instanceId: 'schema-editor-1', // MANDATORY — must be unique per panel
mcpServerUrl: '...'
});Throwing if instanceId is missing:
// v2.0.0 throws for all of these:
initializeStructuraWebview({}); // throws: "instanceId is REQUIRED"
createSchemaBuilder({}); // throws: "instanceId is required"
create_redux_store(config, undefined); // throws: "instanceId must be non-empty"
getInstanceReduxStore(''); // throws: "requires a non-empty instanceId"Key Points:
- Type system now enforces
instanceId: string(non-optional) at compile time - Runtime validation throws with clear error messages if
instanceIdis empty or missing - No fallback behavior — every instance is explicit
- Deterministic and testable: same
instanceIdalways produces same localStorage namespace