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
4 changes: 2 additions & 2 deletions .github/workflows/website-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
pull_request:
paths:
- "website/**"
- "openhcs/resources/assets/openhcs-mark.svg"
- "openhcs/resources/assets/openhcs-*.svg"
- "docs/source/_static/ui.png"
- "scripts/build_website.py"
- "tests/unit/test_build_website.py"
Expand All @@ -14,7 +14,7 @@ on:
branches: [main]
paths:
- "website/**"
- "openhcs/resources/assets/openhcs-mark.svg"
- "openhcs/resources/assets/openhcs-*.svg"
- "docs/source/_static/ui.png"
- "scripts/build_website.py"
- "tests/unit/test_build_website.py"
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

<div align="center">

<img src="openhcs/resources/assets/openhcs-mark.svg" width="140" alt="OpenHCS logo">

<h1>OpenHCS</h1>
<h1><img src="openhcs/resources/assets/openhcs-lockup-stacked.svg" width="190" alt="OpenHCS"></h1>

**Bioimage analysis platform for high-content screening**\
**Compile-time validation · Bidirectional GUI↔Code · Multi-GPU · LLM pipeline generation · Extensible function registry**
Expand Down
2 changes: 1 addition & 1 deletion openhcs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from openhcs._source_dependencies import ensure_source_checkout_external_paths

__version__ = "0.7.2"
__version__ = "0.7.3"

# Configure polystore defaults for OpenHCS integration
os.environ.setdefault("POLYSTORE_METADATA_FILENAME", "openhcs_metadata.json")
Expand Down
2 changes: 1 addition & 1 deletion openhcs/pyqt_gui/branding.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ def openhcs_application_icon() -> QIcon:
"""Build the OpenHCS application icon from its packaged raster asset."""

pixmap = QPixmap()
if not pixmap.loadFromData(brand_asset_bytes(BrandAsset.RASTER)):
if not pixmap.loadFromData(brand_asset_bytes(BrandAsset.ICON_RASTER)):
raise RuntimeError("Packaged OpenHCS application icon could not be decoded.")
return QIcon(pixmap)
13 changes: 9 additions & 4 deletions openhcs/resources/assets/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
# OpenHCS brand assets

`openhcs-mark.svg` is the canonical OpenHCS project mark. It preserves the
geometry and colors of the OpenHCSDev organization avatar.
The official source family contains:

`openhcs-mark.png`, `openhcs.ico`, and `openhcs.icns` are mechanically rendered
platform encodings of that SVG. Regenerate them with:
- `openhcs-mark.svg` and `openhcs-mark-mono.svg` for symbol-only use.
- `openhcs-lockup-horizontal.svg` for wide headers.
- `openhcs-lockup-stacked.svg` for compact full-logo placement.
- `openhcs-icon-square.svg` for application and plugin icons.
- `openhcs-favicon.svg` for browser chrome.

`openhcs-icon-square.png`, `openhcs.ico`, and `openhcs.icns` are mechanically
rendered platform encodings of `openhcs-icon-square.svg`. Regenerate them with:

```bash
scripts/render_brand_assets.sh
Expand Down
5 changes: 5 additions & 0 deletions openhcs/resources/assets/openhcs-favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added openhcs/resources/assets/openhcs-icon-square.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions openhcs/resources/assets/openhcs-icon-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions openhcs/resources/assets/openhcs-lockup-horizontal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions openhcs/resources/assets/openhcs-lockup-stacked.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions openhcs/resources/assets/openhcs-mark-mono.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed openhcs/resources/assets/openhcs-mark.png
Binary file not shown.
11 changes: 4 additions & 7 deletions openhcs/resources/assets/openhcs-mark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified openhcs/resources/assets/openhcs.icns
Binary file not shown.
Binary file modified openhcs/resources/assets/openhcs.ico
Binary file not shown.
16 changes: 12 additions & 4 deletions openhcs/resources/brand.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@
from pathlib import Path


class BrandAsset(str, Enum):
"""Closed set of mechanically equivalent OpenHCS mark encodings."""
BRAND_PRIMARY_COLOR = "#1D9E75"


SCALABLE = "openhcs-mark.svg"
RASTER = "openhcs-mark.png"
class BrandAsset(str, Enum):
"""Closed family of official OpenHCS logo variants and native encodings."""

MARK = "openhcs-mark.svg"
MARK_MONO = "openhcs-mark-mono.svg"
LOCKUP_HORIZONTAL = "openhcs-lockup-horizontal.svg"
LOCKUP_STACKED = "openhcs-lockup-stacked.svg"
ICON_SQUARE = "openhcs-icon-square.svg"
FAVICON = "openhcs-favicon.svg"
ICON_RASTER = "openhcs-icon-square.png"
WINDOWS_ICON = "openhcs.ico"
MACOS_ICON = "openhcs.icns"

Expand Down
21 changes: 0 additions & 21 deletions packaging/chatgpt/openhcs/assets/openhcs.svg

This file was deleted.

2 changes: 1 addition & 1 deletion packaging/codex/openhcs/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openhcs",
"version": "0.7.2",
"version": "0.7.3",
"description": "Inspect, author, validate, and run OpenHCS microscopy workflows through the local OpenHCS MCP server.",
"author": {
"name": "OpenHCSDev",
Expand Down
2 changes: 1 addition & 1 deletion packaging/codex/openhcs/.mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"command": "uvx",
"args": [
"--from",
"openhcs[gui,mcp,viz]==0.7.2",
"openhcs[gui,mcp,viz]==0.7.3",
"openhcs-mcp"
]
}
Expand Down
2 changes: 1 addition & 1 deletion packaging/mcpb/openhcs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": "0.4",
"name": "openhcs",
"display_name": "OpenHCS",
"version": "0.7.2",
"version": "0.7.3",
"description": "Operate local OpenHCS microscopy workflows through MCP.",
"long_description": "Installs a pinned OpenHCS environment with the MCP server, PyQt user interface, and Napari/Fiji viewer runtimes. The MCP process runs over stdio and attaches to an authenticated, separately running OpenHCS UI when GUI tools are requested.",
"author": {
Expand Down
4 changes: 2 additions & 2 deletions packaging/mcpb/openhcs/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[project]
name = "openhcs-mcpb-runtime"
version = "0.7.2"
version = "0.7.3"
description = "Managed runtime for the OpenHCS MCP Bundle"
requires-python = ">=3.11,<3.14"
dependencies = [
"openhcs[gui,mcp,viz]==0.7.2",
"openhcs[gui,mcp,viz]==0.7.3",
]
5 changes: 3 additions & 2 deletions scripts/build_hosted_mcp_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@

from openhcs.mcp.http import hosted_capability_registry
from openhcs.mcp.http_auth import McpHttpAuthenticationMode
from openhcs.resources.brand import BRAND_PRIMARY_COLOR


REPO_ROOT = Path(__file__).resolve().parents[1]
LOCAL_PLUGIN_ROOT = REPO_ROOT / "packaging" / "codex" / "openhcs"
LOCAL_PLUGIN_MANIFEST = LOCAL_PLUGIN_ROOT / ".codex-plugin" / "plugin.json"
CHATGPT_PLUGIN_ASSET = (
REPO_ROOT / "packaging" / "chatgpt" / "openhcs" / "assets" / "openhcs.svg"
REPO_ROOT / "openhcs" / "resources" / "assets" / "openhcs-icon-square.svg"
)

APP_MANIFEST_PATH = "./.app.json"
Expand Down Expand Up @@ -328,7 +329,7 @@ def _plugin_manifest(
"privacyPolicyURL": privacy_policy_url,
"termsOfServiceURL": terms_of_service_url,
"defaultPrompt": list(STARTER_PROMPTS),
"brandColor": "#00A86B",
"brandColor": BRAND_PRIMARY_COLOR,
"composerIcon": validate_plugin_relative_path(
BRAND_ASSET_PATH,
label="Plugin composer-icon path",
Expand Down
10 changes: 9 additions & 1 deletion scripts/build_website.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@
"assets/logos/tensorflow.svg",
)
ASSET_SOURCES = {
"assets/logos/openhcs.svg": "openhcs/resources/assets/openhcs-mark.svg",
"assets/logos/openhcs-favicon.svg": (
"openhcs/resources/assets/openhcs-favicon.svg"
),
"assets/logos/openhcs-horizontal.svg": (
"openhcs/resources/assets/openhcs-lockup-horizontal.svg"
),
"assets/logos/openhcs-stacked.svg": (
"openhcs/resources/assets/openhcs-lockup-stacked.svg"
),
"assets/ui.png": "docs/source/_static/ui.png",
}
REQUIRED_COPY = (
Expand Down
4 changes: 2 additions & 2 deletions scripts/render_brand_assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ set -euo pipefail

repository_root=$(cd "$(dirname "$0")/.." && pwd)
asset_directory="$repository_root/openhcs/resources/assets"
source_svg="$asset_directory/openhcs-mark.svg"
raster_png="$asset_directory/openhcs-mark.png"
source_svg="$asset_directory/openhcs-icon-square.svg"
raster_png="$asset_directory/openhcs-icon-square.png"
windows_icon="$asset_directory/openhcs.ico"
macos_icon="$asset_directory/openhcs.icns"

Expand Down
6 changes: 3 additions & 3 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
"url": "https://github.com/OpenHCSDev/OpenHCS",
"source": "github"
},
"version": "0.7.2",
"version": "0.7.3",
"packages": [
{
"registryType": "pypi",
"registryBaseUrl": "https://pypi.org",
"identifier": "openhcs",
"version": "0.7.2",
"version": "0.7.3",
"runtimeHint": "uvx",
"runtimeArguments": [
{
"type": "named",
"name": "--with",
"value": "openhcs[gui,mcp,viz]==0.7.2",
"value": "openhcs[gui,mcp,viz]==0.7.3",
"description": "Install the matching OpenHCS GUI, MCP, Napari, and Fiji dependencies."
}
],
Expand Down
72 changes: 57 additions & 15 deletions tests/unit/test_brand_assets.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
from __future__ import annotations

import re
import struct
from xml.etree import ElementTree

from openhcs.resources.brand import (
BRAND_PRIMARY_COLOR,
BrandAsset,
brand_asset_bytes,
brand_asset_path,
)


def test_primary_brand_color_matches_official_mark():
assert BRAND_PRIMARY_COLOR == "#1D9E75"


def test_brand_assets_are_one_complete_packaged_family() -> None:
assert {asset.name for asset in BrandAsset} == {
"SCALABLE",
"RASTER",
"MARK",
"MARK_MONO",
"LOCKUP_HORIZONTAL",
"LOCKUP_STACKED",
"ICON_SQUARE",
"FAVICON",
"ICON_RASTER",
"WINDOWS_ICON",
"MACOS_ICON",
}
Expand All @@ -23,26 +34,57 @@ def test_brand_assets_are_one_complete_packaged_family() -> None:
assert path.read_bytes() == brand_asset_bytes(asset)


def test_canonical_brand_svg_preserves_official_geometry_and_colors() -> None:
root = ElementTree.fromstring(brand_asset_bytes(BrandAsset.SCALABLE))
def test_official_logo_family_preserves_declared_geometry_and_colors() -> None:
root = ElementTree.fromstring(brand_asset_bytes(BrandAsset.MARK))
namespace = {"svg": "http://www.w3.org/2000/svg"}

assert root.attrib["viewBox"] == "0 0 420 420"
background = root.find("svg:rect", namespace)
mark = root.find("svg:path", namespace)
assert root.attrib["viewBox"] == "0 0 90 32"
elements = list(root)
assert len(elements) == 3
assert elements[0].attrib["stroke"] == "#1D9E75"
assert elements[1].attrib["stroke"] == "#5DCAA5"
assert elements[2].attrib["fill"] == "#1D9E75"

square = ElementTree.fromstring(brand_asset_bytes(BrandAsset.ICON_SQUARE))
assert square.attrib["viewBox"] == "0 0 512 512"
background = square.find("svg:rect", namespace)
assert background is not None
assert background.attrib["fill"] == "#0A0D16"

for asset in (
BrandAsset.MARK,
BrandAsset.LOCKUP_HORIZONTAL,
BrandAsset.LOCKUP_STACKED,
BrandAsset.ICON_SQUARE,
BrandAsset.FAVICON,
):
assert b"#dda98b" not in brand_asset_bytes(asset).lower()


def test_stacked_wordmark_and_symbol_share_flush_width() -> None:
root = ElementTree.fromstring(brand_asset_bytes(BrandAsset.LOCKUP_STACKED))
namespace = {"svg": "http://www.w3.org/2000/svg"}
text = root.find("svg:text", namespace)
mark = root.find("svg:g", namespace)

assert text is not None
assert mark is not None
assert background.attrib == {
"width": "420",
"height": "420",
"fill": "#f0f0f0",
}
assert mark.attrib["fill"] == "#dda98b"
assert mark.attrib["d"].startswith("M35 35h141")
assert text.attrib["x"] == "7"
assert text.attrib["textLength"] == "140"
assert text.attrib["lengthAdjust"] == "spacingAndGlyphs"

match = re.fullmatch(
r"translate\((?P<x>[0-9.]+) (?P<y>[0-9.]+)\) "
r"scale\((?P<scale>[0-9.]+)\)",
mark.attrib["transform"],
)
assert match is not None
assert float(match.group("x")) == float(text.attrib["x"])
assert abs(90 * float(match.group("scale")) - 140) < 0.01


def test_platform_brand_encodings_have_native_container_headers() -> None:
png = brand_asset_bytes(BrandAsset.RASTER)
png = brand_asset_bytes(BrandAsset.ICON_RASTER)
assert png[:8] == b"\x89PNG\r\n\x1a\n"
assert struct.unpack(">II", png[16:24]) == (1024, 1024)

Expand Down
Loading
Loading