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
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**/.git* annex.largefiles=nothing
* annex.largefiles=((mimeencoding=binary)and(largerthan=0))
*.svg annex.largefiles=(largerthan=100K)
# Preserve the storage rule for the original SVG in this branch's history.
2026-usrse/orinoco-lite-poster.svg annex.largefiles=nothing
# Preserve the original Git-stored logo in PR history; the relocated asset
# under 2026-usrse/orinoco-lite/assets/ follows the normal SVG annex rule.
pics/con-logo-outlined.svg annex.largefiles=nothing
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ repos:
rev: v0.9.1
hooks:
- id: snapper
files: ^2026-usrse/stamped-abcd-poster-draft\.md$
files: ^2026-usrse/(stamped-abcd-poster-draft|orinoco-lite/README)\.md$
9 changes: 8 additions & 1 deletion 2026-usrse/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# make pdf -- builds <name>.pdf for every *.md (needs a PDF engine)
# make html -- explicit alias for the default
# make diagram -- rebuilds the Orinoco metadata-flow figure
# make poster -- prints the editable Orinoco poster HTML to PDF
# make proposal -- just 2026-usrse-con-talk-abstract.html + 2026-usrse-con-talk-abstract.pdf
# make clean -- remove generated *.html / *.pdf
#
Expand Down Expand Up @@ -77,7 +78,7 @@ stamped-abcd-poster-draft.pdf: PDF_LATEX_OPTS := $(subst fontsize=11pt,fontsize=
# PDF via HTML (wkhtmltopdf / weasyprint) — same look as the HTML render
PDF_HTML_OPTS := --to html5 --css=https://cdn.jsdelivr.net/npm/water.css@2/out/water.css

.PHONY: all html pdf diagram proposal clean help check
.PHONY: all html pdf diagram proposal poster clean help check

all: pdf

Expand All @@ -87,6 +88,12 @@ pdf: $(PDFS)

diagram: orinoco-metadata-flow.png

# Browser setup: uv run --with playwright playwright install chromium
# Or use POSTER_BROWSER_ARGS='--channel chrome' for installed Google Chrome.
POSTER_BROWSER_ARGS ?=
poster:
uv run --with playwright python orinoco-lite/build-poster.py $(POSTER_BROWSER_ARGS)

proposal: 2026-usrse-con-talk-abstract.html 2026-usrse-con-talk-abstract.pdf

orinoco-metadata-flow.png: orinoco-metadata-flow.mmd
Expand Down
4 changes: 3 additions & 1 deletion 2026-usrse/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
This is folder to collect ideas/drafts for submissions for https://us-rse.org/usrse26/ as described on https://us-rse.org/usrse26/participate/ .

The Orinoco Lite poster is available as [editable poster](orinoco-lite_poster.html), with [editing, PDF export and release notes](orinoco-lite/README.md).
Its [submitted abstract](orinoco-lite-poster-draft.md) remains alongside it.

### Meeting theme etc

That meeting is about/for Software Research Engineers. And this particular meeting has following
Expand Down Expand Up @@ -46,4 +49,3 @@ scientific fields. So, it would be great to alert audience to our efforts and
efforts of our collaborators (like https://www.distribits.live/ conference
etc).


180 changes: 180 additions & 0 deletions 2026-usrse/orinoco-lite/README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions 2026-usrse/orinoco-lite/assets/con-logo-outlined.svg
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-con.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-lite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-poster.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-upstream.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-video.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions 2026-usrse/orinoco-lite/assets/orinoco-qr-workflow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions 2026-usrse/orinoco-lite/assets/psychoinformatics_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
396 changes: 396 additions & 0 deletions 2026-usrse/orinoco-lite/assets/repronim_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions 2026-usrse/orinoco-lite/build-poster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2026 Center for Open Neuroscience
# SPDX-License-Identifier: Apache-2.0
"""Print the editable HTML poster to PDF and optionally save a screen preview.

From the repository root:
uv run --with playwright playwright install chromium # once
uv run --with playwright python 2026-usrse/orinoco-lite/build-poster.py

Use --channel chrome to use an installed Google Chrome instead.
"""

from __future__ import annotations

import argparse
import asyncio
from pathlib import Path

from playwright.async_api import async_playwright


async def build(channel: str | None, preview: Path | None, font_proof: bool = False) -> None:
source = Path(__file__).resolve().parent.parent / "orinoco-lite_poster.html"
output = Path(__file__).with_name("poster.pdf")
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(channel=channel)
page = await browser.new_page(viewport={"width": 1640, "height": 1300})
await page.goto(source.as_uri(), wait_until="networkidle")
await page.evaluate("document.fonts.ready")
problems = await page.evaluate("""() => {
const issues = [];
for (const img of document.images) {
if (!img.complete || !img.naturalWidth) issues.push(`Missing image: ${img.getAttribute('src')}`);
}
for (const el of document.querySelectorAll('.poster, .columns, .comparison, .deployment, .outcomes, .column-head, .masthead, .footer, .component-figure, .component-art, .website-evidence, .shared-editor, .qr-badge')) {
if (el.scrollHeight > el.clientHeight + 2 || el.scrollWidth > el.clientWidth + 2) {
const label = el.querySelector('h2')?.innerText || el.className;
issues.push(`Overflow: ${label} (${el.scrollWidth}×${el.scrollHeight} in ${el.clientWidth}×${el.clientHeight}px)`);
}
}
if (document.querySelectorAll('h1').length !== 1) issues.push('Expected one poster title');
return issues;
}""")
if preview:
await page.locator(".poster").screenshot(path=str(preview))
if problems:
await browser.close()
raise RuntimeError("\n".join(problems))
await page.pdf(
path=str(output),
print_background=True,
prefer_css_page_size=True,
display_header_footer=False,
margin={"top": "0", "right": "0", "bottom": "0", "left": "0"},
)
if font_proof:
# Use an actual poster sentence and family, with physical point sizes.
from html import escape

paragraph = await page.locator('.graph-description').inner_text()
sample = escape(paragraph.split('. ', 1)[0] + '.')
family = await page.locator('.graph-description').evaluate(
"el => getComputedStyle(el).fontFamily"
)
body_pt = float(await page.locator('.graph-description').evaluate(
"el => parseFloat(getComputedStyle(el).fontSize)"
)) * 0.75
blocks = ''.join(
f'<section><h2>{size} pt{label}</h2>'
f'<p style="font-size:{size}pt">{sample}</p></section>'
for size, label in [(27, ' — larger comparison'), (25.5, ' — intermediate comparison'), (24, ' — smaller comparison')]
)
await page.set_content(f'''<!doctype html><html lang="en"><head>
<meta charset="utf-8"><title>ORINOCO poster type-size proof</title>
<style>
@page {{ size: letter; margin: .55in; }}
* {{ box-sizing: border-box; }}
body {{ margin: 0; font-family: {family}; color: #193c4d; }}
h1 {{ font-size: 18pt; margin: 0 0 10pt; }}
.instructions {{ font-size: 10pt; line-height: 1.4; margin: 0 0 20pt; }}
section {{ background: #f6f2e9; border-top: 2pt solid #286857;
padding: 10pt 12pt; margin-bottom: 14pt; break-inside: avoid; }}
h2 {{ font-size: 11pt; margin: 0 0 9pt; color: #286857; }}
section p {{ line-height: 1.35; margin: 0; }}
.scale {{ width: 2in; border: solid #193c4d; border-width: 2pt 2pt 0;
height: 8pt; margin-top: 6pt; }}
.note {{ font-size: 9pt; line-height: 1.4; }}
</style></head><body>
<h1>Compare the poster's printed text sizes</h1>
<p class="instructions">Print this US Letter page at <b>Actual size / 100%</b>.
Do not use “Fit to page”. Read it from the distance you expect at the poster,
around one metre, and choose a size that is comfortable without leaning in.
The wording, font family, line spacing and colors match the poster;
this sheet uses narrower lines to fit ordinary paper.</p>
{blocks}
<div class="scale"></div><p class="note">This line should measure exactly 2 inches (50.8 mm).</p>
<p class="note">PosterNerd recommends at least 24 pt for text and 36 pt for headings.
Current main paragraphs are {body_pt:g} pt.
Source: posternerd.com/tutorials/customer-questions</p>
</body></html>''')
proof = Path(__file__).with_name('font-proof.pdf')
await page.pdf(path=str(proof), print_background=True, prefer_css_page_size=True,
display_header_footer=False)
print(f"Created {proof}")
await browser.close()
print(f"Created {output} ({output.stat().st_size / 1e6:.2f} MB)")


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--channel", help="Browser channel, e.g. chrome; default: Playwright Chromium")
parser.add_argument("--preview", type=Path, help="Optional PNG preview path")
parser.add_argument("--font-proof", action="store_true", help="Also export a printable Letter-size type comparison")
args = parser.parse_args()
asyncio.run(build(args.channel, args.preview, args.font_proof))
107 changes: 107 additions & 0 deletions 2026-usrse/orinoco-lite/make-component-diagram.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2026 Center for Open Neuroscience
# SPDX-License-Identifier: Apache-2.0
"""Adapt Stephan Heunis's ORINOCO component artwork for the Lite deployment.

Run from any directory with Python 3. The source illustration remains unchanged.
The output is a self-contained SVG; its new labels remain ordinary editable text.
"""
from copy import deepcopy
from pathlib import Path
import xml.etree.ElementTree as ET

SVG = "http://www.w3.org/2000/svg"
XLINK = "http://www.w3.org/1999/xlink"
ET.register_namespace("", SVG)
ET.register_namespace("xlink", XLINK)
ROOT = Path(__file__).resolve().parent
SOURCE = ROOT / "assets/orinoco-selfhost-components-poster.svg"
OUTPUT = ROOT / "assets/orinoco-lite-components-poster.svg"


def main():
source = ET.parse(SOURCE).getroot()
diagram = ET.Element(f"{{{SVG}}}svg", {
"viewBox": "0 0 678.08 304.58", "width": "678.08", "height": "304.58",
"role": "img", "aria-labelledby": "lite-title lite-description",
})
ET.SubElement(diagram, f"{{{SVG}}}title", {"id": "lite-title"}).text = "ORINOCO Lite components"
ET.SubElement(diagram, f"{{{SVG}}}desc", {"id": "lite-description"}).text = (
"A shared schema defines the SHACL Vue editor. The editor proposes changes "
"through the shared App to GitHub pull requests. Reviewers merge accepted "
"changes into Git-backed YAML records. GitHub Actions runs ORINOCO tools to "
"build reusable outputs, illustrated by a catalog, website and report. "
"Adapted from ORINOCO artwork by Stephan Heunis, CC BY 4.0."
)
defs = deepcopy(source.find(f"{{{SVG}}}defs"))
diagram.append(defs)
marker = ET.SubElement(defs, f"{{{SVG}}}marker", {
"id": "lite-arrowhead", "viewBox": "0 0 10 10", "refX": "9", "refY": "5",
"markerWidth": "5", "markerHeight": "5", "orient": "auto-start-reverse",
})
ET.SubElement(marker, f"{{{SVG}}}path", {"d": "M 1 1 L 9 5 L 1 9", "fill": "none", "stroke": "#009900", "stroke-width": "1.6"})
# Keep the original schema, editor, processing and output artwork in place.
# Remove Forgejo, Dump Things, and the old integration/storage arrows.
removed = {
"g6835", # Forgejo
"g1", # Dump Things truck, logo and label
"layer1-5-3-1-8-5-3-2", "layer1-5-3-1-8-5-3-2-7", # editor/storage pair
"g36094", "g36094-2", # Forgejo integration pairs
"g34511-8", # schema arrow; its replacement leads directly to the editor
}
scale = ET.SubElement(diagram, f"{{{SVG}}}g", {"transform": "scale(3.7795275591)"})
layer = deepcopy(source.find(f"{{{SVG}}}g"))
for child in list(layer):
if child.get("id") in removed:
layer.remove(child)
scale.append(layer)
# Overlay coordinates use CSS pixels, corresponding to the original canvas.
overlay = ET.fromstring('''<g xmlns="http://www.w3.org/2000/svg" font-family="Arial, Helvetica, sans-serif" fill="#193c4d">
<g id="lite-connections" fill="none" stroke="#009900" stroke-width="1.8" marker-end="url(#lite-arrowhead)">
<path d="M 134,144 C 165,143 163,92 190,80"/>
<path d="M 289,93 C 309,98 314,109 324,119"/>
<path d="M 351,169 C 341,195 301,207 275,216"/>
<path d="M 278,243 C 367,281 423,235 468,168"/>
</g>
<g id="lite-connection-labels" font-size="11" text-anchor="middle">
<text x="150" y="105">defines</text>
<text x="335" y="84">propose</text>
<text x="353" y="98" font-size="9.5">via shared App</text>
<text x="319" y="185">merge</text>
<text x="382" y="265">build</text>
</g>
<g id="github-pull-requests" transform="translate(295 115)">
<circle cx="22" cy="26" r="22" fill="#193c4d"/>
<g fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="15" cy="14" r="3"/><circle cx="15" cy="37" r="3"/>
<circle cx="31" cy="37" r="3"/><path d="M 15,17 V 34 M 31,34 V 23 Q 31,14 24,14 H 22 M 25,10 L 21,14 L 25,18"/>
</g>
<text x="51" y="31" font-size="25" font-weight="700">GitHub</text>
<text x="78" y="53" font-size="13" text-anchor="middle">pull requests</text>
</g>
<g id="git-yaml-records" transform="translate(206 198)">
<path d="M 15,5 H 52 L 66,19 V 62 H 15 Z" fill="#ffeed0" stroke="#193c4d" stroke-width="2"/>
<path d="M 52,5 V 19 H 66" fill="none" stroke="#193c4d" stroke-width="2"/>
<g fill="none" stroke="#b55c28" stroke-width="2.5" stroke-linecap="round">
<path d="M 28,29 V 48 M 28,41 Q 42,41 44,29"/>
<circle cx="28" cy="27" r="3" fill="#ffeed0"/><circle cx="28" cy="51" r="3" fill="#ffeed0"/>
<circle cx="45" cy="26" r="3" fill="#ffeed0"/>
</g>
<text x="40" y="80" font-size="15" text-anchor="middle" font-weight="700">Git + YAML</text>
<text x="40" y="95" font-size="11" text-anchor="middle">accepted records</text>
</g>
<g id="actions-processing" font-size="12" text-anchor="middle">
<text x="489" y="91" font-weight="700">GitHub Actions</text>
<text x="489" y="106">ORINOCO tools</text>
</g>
</g>''')
diagram.append(overlay)
# Avoid following a git-annex symlink when regenerating a staged asset.
if OUTPUT.is_symlink():
OUTPUT.unlink()
ET.ElementTree(diagram).write(OUTPUT, encoding="utf-8", xml_declaration=True)
print(f"Created {OUTPUT}")


if __name__ == "__main__":
main()
Loading
Loading