ourui 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {ourui-0.2.1 → ourui-0.2.2}/PKG-INFO +2 -2
  2. {ourui-0.2.1 → ourui-0.2.2}/README.md +1 -1
  3. {ourui-0.2.1 → ourui-0.2.2}/ourui/emit/html.py +72 -5
  4. {ourui-0.2.1 → ourui-0.2.2}/ourui/pipeline.py +4 -1
  5. {ourui-0.2.1 → ourui-0.2.2}/ourui.egg-info/PKG-INFO +2 -2
  6. {ourui-0.2.1 → ourui-0.2.2}/pyproject.toml +1 -1
  7. {ourui-0.2.1 → ourui-0.2.2}/LICENSE +0 -0
  8. {ourui-0.2.1 → ourui-0.2.2}/ourui/__init__.py +0 -0
  9. {ourui-0.2.1 → ourui-0.2.2}/ourui/analysis/__init__.py +0 -0
  10. {ourui-0.2.1 → ourui-0.2.2}/ourui/analysis/components.py +0 -0
  11. {ourui-0.2.1 → ourui-0.2.2}/ourui/cli.py +0 -0
  12. {ourui-0.2.1 → ourui-0.2.2}/ourui/design/__init__.py +0 -0
  13. {ourui-0.2.1 → ourui-0.2.2}/ourui/design/resolve.py +0 -0
  14. {ourui-0.2.1 → ourui-0.2.2}/ourui/emit/__init__.py +0 -0
  15. {ourui-0.2.1 → ourui-0.2.2}/ourui/emit/js.py +0 -0
  16. {ourui-0.2.1 → ourui-0.2.2}/ourui/lowering/__init__.py +0 -0
  17. {ourui-0.2.1 → ourui-0.2.2}/ourui/lowering/intent.py +0 -0
  18. {ourui-0.2.1 → ourui-0.2.2}/ourui/lowering/layout.py +0 -0
  19. {ourui-0.2.1 → ourui-0.2.2}/ourui/lowering/presentation.py +0 -0
  20. {ourui-0.2.1 → ourui-0.2.2}/ourui/lowering/render.py +0 -0
  21. {ourui-0.2.1 → ourui-0.2.2}/ourui/lsp/__init__.py +0 -0
  22. {ourui-0.2.1 → ourui-0.2.2}/ourui/lsp/completions.py +0 -0
  23. {ourui-0.2.1 → ourui-0.2.2}/ourui/lsp/protocol.py +0 -0
  24. {ourui-0.2.1 → ourui-0.2.2}/ourui/lsp/server.py +0 -0
  25. {ourui-0.2.1 → ourui-0.2.2}/ourui/node.py +0 -0
  26. {ourui-0.2.1 → ourui-0.2.2}/ourui/parse/__init__.py +0 -0
  27. {ourui-0.2.1 → ourui-0.2.2}/ourui/runtime/__init__.py +0 -0
  28. {ourui-0.2.1 → ourui-0.2.2}/ourui/runtime/hmr.py +0 -0
  29. {ourui-0.2.1 → ourui-0.2.2}/ourui/runtime/invoke.py +0 -0
  30. {ourui-0.2.1 → ourui-0.2.2}/ourui/runtime/session.py +0 -0
  31. {ourui-0.2.1 → ourui-0.2.2}/ourui/serialize/__init__.py +0 -0
  32. {ourui-0.2.1 → ourui-0.2.2}/ourui/theme.py +0 -0
  33. {ourui-0.2.1 → ourui-0.2.2}/ourui/ui.py +0 -0
  34. {ourui-0.2.1 → ourui-0.2.2}/ourui.egg-info/SOURCES.txt +0 -0
  35. {ourui-0.2.1 → ourui-0.2.2}/ourui.egg-info/dependency_links.txt +0 -0
  36. {ourui-0.2.1 → ourui-0.2.2}/ourui.egg-info/entry_points.txt +0 -0
  37. {ourui-0.2.1 → ourui-0.2.2}/ourui.egg-info/top_level.txt +0 -0
  38. {ourui-0.2.1 → ourui-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ourui
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Python-first UI compiler — write intent in Python, emit HTML/CSS/JS
5
5
  Author: OurUI contributors
6
6
  License: MIT License
@@ -46,7 +46,7 @@ Description-Content-Type: text/markdown
46
46
  License-File: LICENSE
47
47
  Dynamic: license-file
48
48
 
49
- # ourui 0.2.1
49
+ # ourui 0.2.2
50
50
 
51
51
  Python package for the **OurUI** compiler and runtime.
52
52
 
@@ -1,4 +1,4 @@
1
- # ourui 0.2.1
1
+ # ourui 0.2.2
2
2
 
3
3
  Python package for the **OurUI** compiler and runtime.
4
4
 
@@ -98,6 +98,60 @@ button.ourui-tone-muted {
98
98
  color: var(--ourui-muted-fg);
99
99
  }
100
100
  """
101
+ # Tone classes above use CSS vars seeded from Resolved Design (Host Contract).
102
+ # Per-node rules from Resolved Design override for concrete fill/fg/radius/pad.
103
+
104
+
105
+ def _as_resolved_design(resolved_design: Any) -> dict[str, Any] | None:
106
+ if resolved_design is None:
107
+ return None
108
+ if hasattr(resolved_design, "to_dict"):
109
+ return resolved_design.to_dict()
110
+ return dict(resolved_design)
111
+
112
+
113
+ def _tokens_from_resolved(rd: dict[str, Any]) -> dict[str, dict[str, str]]:
114
+ tokens = rd.get("tokens") or {}
115
+ return {
116
+ "light": dict(tokens.get("light") or {}),
117
+ "dark": dict(tokens.get("dark") or {}),
118
+ }
119
+
120
+
121
+ def _emit_resolved_node_css(rd: dict[str, Any]) -> str:
122
+ """Host maps Resolved Design node values → CSS (no Design System tables)."""
123
+ lines: list[str] = []
124
+ nodes = rd.get("nodes") or {}
125
+ for nid in sorted(nodes):
126
+ node = nodes[nid]
127
+ role = node.get("role")
128
+ resolved = node.get("resolved") or {}
129
+ if role not in {"button", "link"}:
130
+ continue
131
+ fill = resolved.get("fill")
132
+ fg = resolved.get("fg")
133
+ if not fill and not fg:
134
+ continue
135
+ lines.append(f'[data-ourui-id="{nid}"] {{')
136
+ if role == "button":
137
+ if fill:
138
+ lines.append(f" background: {fill};")
139
+ if fg:
140
+ lines.append(f" color: {fg};")
141
+ if resolved.get("radius"):
142
+ lines.append(f" border-radius: {resolved['radius']};")
143
+ pad_b = resolved.get("pad_block")
144
+ pad_i = resolved.get("pad_inline")
145
+ if pad_b and pad_i:
146
+ lines.append(f" padding: {pad_b} {pad_i};")
147
+ lines.append(" border-color: transparent;")
148
+ else:
149
+ if fill:
150
+ lines.append(f" color: {fill};")
151
+ lines.append("}")
152
+ if not lines:
153
+ return ""
154
+ return "\n".join(lines) + "\n"
101
155
 
102
156
 
103
157
  def _tone_name(attrs: dict[str, Any]) -> str | None:
@@ -232,7 +286,16 @@ def apply_state_values(rtr: dict[str, Any], state_values: dict[str, Any] | None)
232
286
  return out
233
287
 
234
288
 
235
- def emit_css(tokens: dict[str, dict[str, str]] | None = None) -> str:
289
+ def emit_css(
290
+ tokens: dict[str, dict[str, str]] | None = None,
291
+ *,
292
+ resolved_design: Any = None,
293
+ ) -> str:
294
+ """Emit host CSS. Prefer Resolved Design tokens + per-node rules (Host Contract)."""
295
+ rd = _as_resolved_design(resolved_design)
296
+ if rd is not None:
297
+ token_block = emit_tokens_css(_tokens_from_resolved(rd))
298
+ return token_block + _BASE_CSS + _emit_resolved_node_css(rd)
236
299
  token_block = emit_tokens_css(tokens or default_tokens())
237
300
  return token_block + _BASE_CSS
238
301
 
@@ -244,8 +307,9 @@ def emit_html_document(
244
307
  state_values: dict[str, Any] | None = None,
245
308
  hmr: bool = False,
246
309
  tokens: dict[str, dict[str, str]] | None = None,
310
+ resolved_design: Any = None,
247
311
  ) -> str:
248
- """Emit a full HTML document from an RTR dict (HostNode tree only)."""
312
+ """Emit HTML from Host Contract inputs: RTR + Resolved Design (+ optional tokens fallback)."""
249
313
  rtr = apply_state_values(rtr, state_values)
250
314
  nodes = rtr["nodes"]
251
315
  roots = rtr["roots"]
@@ -255,7 +319,7 @@ def emit_html_document(
255
319
  body_lines.append(" </div>")
256
320
 
257
321
  js = emit_js(rtr, hmr=hmr).rstrip("\n")
258
- css = emit_css(tokens).rstrip("\n")
322
+ css = emit_css(tokens, resolved_design=resolved_design).rstrip("\n")
259
323
  parts = [
260
324
  "<!DOCTYPE html>",
261
325
  '<html lang="en">',
@@ -283,10 +347,13 @@ def emit_bundle(
283
347
  *,
284
348
  title: str = "OurUI",
285
349
  tokens: dict[str, dict[str, str]] | None = None,
350
+ resolved_design: Any = None,
286
351
  ) -> dict[str, str]:
287
352
  """Serializable emit artifacts (I10): html + css + js."""
288
353
  return {
289
- "html": emit_html_document(rtr, title=title, tokens=tokens),
290
- "css": emit_css(tokens),
354
+ "html": emit_html_document(
355
+ rtr, title=title, tokens=tokens, resolved_design=resolved_design
356
+ ),
357
+ "css": emit_css(tokens, resolved_design=resolved_design),
291
358
  "js": emit_js(rtr),
292
359
  }
@@ -71,6 +71,7 @@ def compile_dump(path: str | Path) -> dict[str, Any]:
71
71
  "tokens": True,
72
72
  "presentation_graph": True,
73
73
  "resolved_design": True,
74
+ "host_contract": True,
74
75
  },
75
76
  }
76
77
 
@@ -87,7 +88,7 @@ def emit_html(
87
88
  state_values: dict[str, Any] | None = None,
88
89
  hmr: bool = False,
89
90
  ) -> str:
90
- """Compile source → RTR → HTML. Emitter consumes HostNode only."""
91
+ """Compile source → RTR + Resolved Design → HTML (Host Contract)."""
91
92
  artifacts = compile_to_rtr(path, route=route)
92
93
  doc_title = title or Path(path).stem
93
94
  return emit_html_document(
@@ -96,6 +97,7 @@ def emit_html(
96
97
  state_values=state_values,
97
98
  hmr=hmr,
98
99
  tokens=artifacts["semantic_graph"].tokens,
100
+ resolved_design=artifacts["resolved_design"],
99
101
  )
100
102
 
101
103
 
@@ -106,4 +108,5 @@ def emit_all(path: str | Path, *, title: str | None = None) -> dict[str, str]:
106
108
  artifacts["rtr"].to_dict(),
107
109
  title=doc_title,
108
110
  tokens=artifacts["semantic_graph"].tokens,
111
+ resolved_design=artifacts["resolved_design"],
109
112
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ourui
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Python-first UI compiler — write intent in Python, emit HTML/CSS/JS
5
5
  Author: OurUI contributors
6
6
  License: MIT License
@@ -46,7 +46,7 @@ Description-Content-Type: text/markdown
46
46
  License-File: LICENSE
47
47
  Dynamic: license-file
48
48
 
49
- # ourui 0.2.1
49
+ # ourui 0.2.2
50
50
 
51
51
  Python package for the **OurUI** compiler and runtime.
52
52
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ourui"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Python-first UI compiler — write intent in Python, emit HTML/CSS/JS"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes