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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
# Changelog

## [0.2.0] - (2026-03-16)

### Fixes

- **Theme detection**: Fixed auto-theme not responding to host theme changes: removed narrow Marimo shadow DOM gate so the MutationObserver is always active
- **Marimo `dark-theme` class**: Added detection for Marimo 0.20.x `dark-theme` CSS class in addition to Tailwind `dark`

### Improvements

- **Universal theme observer**: Theme auto-detection now works in any host (Marimo, Jupyter, VS Code), observing both `<html>` and `<body>` for class/data-theme changes
- **OS-level theme listener**: Added `prefers-color-scheme` media query change listener for OS dark/light mode switches
- **Dark mode toggle always visible**: Manual toggle no longer hidden in auto-theme mode; clicking it disables auto-theme so the user's choice is respected

## [0.1.4] - (2026-03-16)

### New Features

- **Automatic theme detection**: Auto-detects host dark mode (marimo Tailwind `class="dark"`, `data-theme`, or `prefers-color-scheme`) and syncs the widget theme automatically dark mode toggle is hidden when the host controls the theme
- **Automatic theme detection**: Auto-detects host dark mode (marimo Tailwind `class="dark"`, `data-theme` or `prefers-color-scheme`) and syncs the widget theme automatically; dark mode toggle is hidden when the host controls the theme

### Improvements

Expand All @@ -14,22 +27,22 @@

### New Features

- **Upload button**: Toolbar button to load `.xml` or `.archimate` files directly from the browser no Python code needed to switch models
- **Upload button**: Toolbar button to load `.xml` or `.archimate` files directly from the browser; no Python code needed to switch models
- **Export to SVG**: Download the current diagram as a self-contained SVG file with inlined fonts
- **Export to PNG**: Download the current diagram as a high-resolution (2x) PNG with correct light/dark background

## [0.1.2] - (2026-03-15)

### New Features

- **Demo module**: Built-in `demo_archimate()` function that creates a pre-populated ArchiMate widget with a sample enterprise architecture model (12 elements across Business, Application, and Technology layers) no external files needed
- **Demo module**: Built-in `demo_archimate()` function that creates a pre-populated ArchiMate widget with a sample enterprise architecture model (12 elements across Business, Application and Technology layers); no external files needed

## [0.1.1] - (2026-03-15)

### New Features

- **ArchiMate icons**: Standard SVG icons for all 13 element types (stick figure for BusinessActor, component tabs for ApplicationComponent, 3D box for Node, gear for BusinessFunction, etc.)
- **Filter sidebar**: Collapsible left panel with per-layer, per-element-type, and per-relationship-type checkboxes with counts and "all / none" toggles
- **Filter sidebar**: Collapsible left panel with per-layer, per-element-type and per-relationship-type checkboxes with counts and "all / none" toggles
- **Nest elements**: Composition and Aggregation relationships rendered as visual nesting with a toggle checkbox in the filter panel
- **Archi native format**: Parser now supports both Open Exchange Format XML and Archi tool `.archimate` files

Expand Down Expand Up @@ -59,6 +72,6 @@
- **Element badges**: Two-letter type badges (AC, BA, TS, etc.) on each element
- **Zoom and pan**: Mouse wheel zoom and drag-to-pan on the SVG diagram
- **Dark mode**: Toggle between light and dark themes
- **Details panel**: Click any element to see its name, type, layer, and documentation
- **anywidget integration**: Works in Jupyter, Marimo, and VS Code notebooks
- **Details panel**: Click any element to see its name, type, layer and documentation
- **anywidget integration**: Works in Jupyter, Marimo and VS Code notebooks
- **`from_xml` / `load_xml`**: Load ArchiMate models from file path or XML string
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "anywidget-archimate"
version = "0.1.4"
version = "0.2.0"
description = "Interactive ArchiMate model viewer widget for Jupyter, Marimo, and VS Code notebooks"
readme = "README.md"
license = "Apache-2.0"
Expand Down
37 changes: 27 additions & 10 deletions src/anywidget_archimate/ui/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1054,28 +1054,45 @@ function render({ model, el }) {
wrapper.className = "aam-wrapper";
el.appendChild(wrapper);

// Auto-detect host theme (marimo uses Tailwind class="dark" on <html>)
// Auto-detect host theme (Tailwind dark/dark-theme class, data-theme, or prefers-color-scheme)
function detectHostDark() {
const html = document.documentElement;
if (html.classList.contains("dark")) return true;
if (html.classList.contains("dark") || html.classList.contains("dark-theme")) return true;
if (html.dataset.theme === "dark") return true;
if (document.body?.classList.contains("dark") || document.body?.classList.contains("dark-theme")) return true;
if (document.body?.dataset.theme === "dark") return true;
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) return true;
return false;
}

const hasHostTheme = !!el.getRootNode()?.host?.tagName?.startsWith("MARIMO-");
if (hasHostTheme) {
wrapper.classList.add("aam-auto-theme");
model.set("dark_mode", detectHostDark());
model.save_changes();
const themeObserver = new MutationObserver(() => {
// Always auto-detect and observe theme changes
let autoTheme = true;
model.set("dark_mode", detectHostDark());
model.save_changes();

const themeObserver = new MutationObserver(() => {
if (!autoTheme) return;
const dark = detectHostDark();
if (model.get("dark_mode") !== dark) {
model.set("dark_mode", dark);
model.save_changes();
}
});
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-theme", "style"] });
if (document.body) {
themeObserver.observe(document.body, { attributes: true, attributeFilter: ["class", "data-theme", "style"] });
}

// Also listen for OS-level theme changes
if (window.matchMedia) {
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
if (!autoTheme) return;
const dark = detectHostDark();
if (model.get("dark_mode") !== dark) {
model.set("dark_mode", dark);
model.save_changes();
}
});
themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-theme"] });
}

const toolbar = document.createElement("div");
Expand Down Expand Up @@ -1146,7 +1163,7 @@ function render({ model, el }) {
const s = graphContainer.querySelector("svg");
if (s) { const v = s.getAttribute("viewBox").split(" ").map(Number); s.setAttribute("viewBox", v.join(" ")); }
});
toolbar.querySelector(".aam-btn-dark").addEventListener("click", () => { model.set("dark_mode", !model.get("dark_mode")); model.save_changes(); });
toolbar.querySelector(".aam-btn-dark").addEventListener("click", () => { autoTheme = false; model.set("dark_mode", !model.get("dark_mode")); model.save_changes(); });

// Upload
const fileInput = toolbar.querySelector(".aam-file-input");
Expand Down
5 changes: 1 addition & 4 deletions src/anywidget_archimate/ui/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,4 @@
font-size: 12px;
}

/* Hide dark mode button when host provides theme */
.aam-wrapper.aam-auto-theme .aam-btn-dark {
display: none;
}
/* Dark mode toggle always visible — manual toggle disables auto-theme */
16 changes: 13 additions & 3 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,19 @@ def test_parse_xml_from_file():

def test_layer_map_covers_all_types():
expected_types = {
"ApplicationComponent", "ApplicationInterface", "ApplicationService", "DataObject",
"BusinessActor", "BusinessProcess", "BusinessFunction", "BusinessEvent", "BusinessObject",
"Node", "Device", "SystemSoftware", "TechnologyService",
"ApplicationComponent",
"ApplicationInterface",
"ApplicationService",
"DataObject",
"BusinessActor",
"BusinessProcess",
"BusinessFunction",
"BusinessEvent",
"BusinessObject",
"Node",
"Device",
"SystemSoftware",
"TechnologyService",
}
assert set(LAYER_MAP.keys()) == expected_types

Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading