spaday-studio 0.1.0__py3-none-any.whl
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.
- spaday_studio/__init__.py +46 -0
- spaday_studio/catalog.py +215 -0
- spaday_studio/example.py +90 -0
- spaday_studio/extension/cdn/index.js +2 -0
- spaday_studio/extension/cdn/index.js.map +7 -0
- spaday_studio/extension/css/index.css +1 -0
- spaday_studio/extension/index.html +13 -0
- spaday_studio/index.html +74 -0
- spaday_studio/mcp.py +111 -0
- spaday_studio/models.py +220 -0
- spaday_studio/project.py +100 -0
- spaday_studio/server.py +150 -0
- spaday_studio/session.py +155 -0
- spaday_studio/tests/test_catalog.py +77 -0
- spaday_studio/tests/test_mcp.py +66 -0
- spaday_studio/tests/test_models.py +90 -0
- spaday_studio/tests/test_project.py +55 -0
- spaday_studio/tests/test_server.py +128 -0
- spaday_studio/tests/test_session.py +91 -0
- spaday_studio-0.1.0.dist-info/METADATA +103 -0
- spaday_studio-0.1.0.dist-info/RECORD +24 -0
- spaday_studio-0.1.0.dist-info/WHEEL +4 -0
- spaday_studio-0.1.0.dist-info/entry_points.txt +5 -0
- spaday_studio-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from spaday import ComponentPackage
|
|
4
|
+
|
|
5
|
+
from .catalog import ComponentCatalog, ComponentSchema, ComponentSummary, PropertySchema, discover_catalog
|
|
6
|
+
from .models import (
|
|
7
|
+
InsertNode,
|
|
8
|
+
MoveNode,
|
|
9
|
+
RemoveNode,
|
|
10
|
+
SetProp,
|
|
11
|
+
StudioDocument,
|
|
12
|
+
StudioNode,
|
|
13
|
+
UnsetProp,
|
|
14
|
+
)
|
|
15
|
+
from .project import ProjectFile, export_python
|
|
16
|
+
from .session import PreviewConflict, RevisionConflict, StudioSession, StudioState
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
|
|
20
|
+
package = ComponentPackage(
|
|
21
|
+
name="studio",
|
|
22
|
+
assets_dir=Path(__file__).parent / "extension",
|
|
23
|
+
assets=(("css", "css/index.css"), ("js", "cdn/index.js")),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"ComponentCatalog",
|
|
28
|
+
"ComponentSchema",
|
|
29
|
+
"ComponentSummary",
|
|
30
|
+
"InsertNode",
|
|
31
|
+
"MoveNode",
|
|
32
|
+
"PreviewConflict",
|
|
33
|
+
"ProjectFile",
|
|
34
|
+
"PropertySchema",
|
|
35
|
+
"RemoveNode",
|
|
36
|
+
"RevisionConflict",
|
|
37
|
+
"SetProp",
|
|
38
|
+
"StudioDocument",
|
|
39
|
+
"StudioNode",
|
|
40
|
+
"StudioSession",
|
|
41
|
+
"StudioState",
|
|
42
|
+
"UnsetProp",
|
|
43
|
+
"discover_catalog",
|
|
44
|
+
"export_python",
|
|
45
|
+
"package",
|
|
46
|
+
]
|
spaday_studio/catalog.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Opt-in component catalog discovery for Studio."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
import types
|
|
7
|
+
from importlib import import_module, metadata
|
|
8
|
+
from typing import Any, Literal, cast, get_args, get_origin, get_type_hints
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field, JsonValue
|
|
11
|
+
from spaday import Component
|
|
12
|
+
|
|
13
|
+
PropertyKind = Literal["string", "boolean", "number", "enum", "json"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PropertySchema(BaseModel):
|
|
17
|
+
"""One editable DOM property in a component schema."""
|
|
18
|
+
|
|
19
|
+
name: str
|
|
20
|
+
kind: PropertyKind
|
|
21
|
+
choices: list[JsonValue] = Field(default_factory=list)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ComponentSchema(BaseModel):
|
|
25
|
+
"""The Studio-facing schema for one component tag."""
|
|
26
|
+
|
|
27
|
+
package: str
|
|
28
|
+
tag: str
|
|
29
|
+
class_name: str
|
|
30
|
+
summary: str | None = None
|
|
31
|
+
props: list[PropertySchema] = Field(default_factory=list)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ComponentSummary(BaseModel):
|
|
35
|
+
"""Compact component identity returned by catalog listings."""
|
|
36
|
+
|
|
37
|
+
package: str
|
|
38
|
+
tag: str
|
|
39
|
+
class_name: str
|
|
40
|
+
summary: str | None = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ComponentCatalog(BaseModel):
|
|
44
|
+
"""Available package names and schemas for explicitly selected packages."""
|
|
45
|
+
|
|
46
|
+
available_packages: list[str]
|
|
47
|
+
selected_packages: list[str]
|
|
48
|
+
components: list[ComponentSchema]
|
|
49
|
+
|
|
50
|
+
def component(self, tag: str) -> ComponentSchema:
|
|
51
|
+
"""Return the schema for ``tag`` or raise ``KeyError``."""
|
|
52
|
+
try:
|
|
53
|
+
return next(component for component in self.components if component.tag == tag)
|
|
54
|
+
except StopIteration as error:
|
|
55
|
+
raise KeyError(tag) from error
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_COMMON_PROPS: list[PropertySchema] = [
|
|
59
|
+
PropertySchema(name="id", kind="string"),
|
|
60
|
+
PropertySchema(name="class", kind="string"),
|
|
61
|
+
PropertySchema(name="style", kind="string"),
|
|
62
|
+
PropertySchema(name="title", kind="string"),
|
|
63
|
+
PropertySchema(name="hidden", kind="boolean"),
|
|
64
|
+
]
|
|
65
|
+
_TEXT_PROP = PropertySchema(name="textContent", kind="string")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def discover_catalog(packages: tuple[str, ...] | list[str] = ()) -> ComponentCatalog:
|
|
69
|
+
"""Discover names without importing them and schemas only for selected packages."""
|
|
70
|
+
entry_points = tuple(metadata.entry_points(group="spaday.component_packages"))
|
|
71
|
+
available = sorted(entry_point.name for entry_point in entry_points if entry_point.name != "studio")
|
|
72
|
+
components: list[ComponentSchema] = _html_components()
|
|
73
|
+
selected: list[str] = []
|
|
74
|
+
|
|
75
|
+
for reference in packages:
|
|
76
|
+
if ":" in reference:
|
|
77
|
+
module_name, _, attribute = reference.partition(":")
|
|
78
|
+
module = import_module(module_name)
|
|
79
|
+
descriptor = getattr(module, attribute)
|
|
80
|
+
package_name = descriptor.name
|
|
81
|
+
else:
|
|
82
|
+
matches = [entry_point for entry_point in entry_points if entry_point.name == reference]
|
|
83
|
+
if len(matches) != 1:
|
|
84
|
+
raise ValueError(f"component package {reference!r} does not have exactly one installed entry point")
|
|
85
|
+
package_name = reference
|
|
86
|
+
module = import_module(matches[0].module)
|
|
87
|
+
selected.append(package_name)
|
|
88
|
+
components.extend(_module_components(package_name, module))
|
|
89
|
+
|
|
90
|
+
components.sort(key=lambda component: (component.package, component.tag))
|
|
91
|
+
return ComponentCatalog(
|
|
92
|
+
available_packages=available,
|
|
93
|
+
selected_packages=selected,
|
|
94
|
+
components=components,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _module_components(package: str, module: types.ModuleType) -> list[ComponentSchema]:
|
|
99
|
+
schemas: list[ComponentSchema] = []
|
|
100
|
+
seen: set[str] = set()
|
|
101
|
+
for name in getattr(module, "__all__", ()):
|
|
102
|
+
value = getattr(module, name, None)
|
|
103
|
+
if not isinstance(value, type) or not issubclass(value, Component) or value is Component or not value.tag or value.tag in seen:
|
|
104
|
+
continue
|
|
105
|
+
seen.add(value.tag)
|
|
106
|
+
schemas.append(_class_schema(package, name, value))
|
|
107
|
+
return schemas
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _class_schema(package: str, name: str, component_class: type[Component]) -> ComponentSchema:
|
|
111
|
+
signature = inspect.signature(component_class.__init__)
|
|
112
|
+
try:
|
|
113
|
+
hints = get_type_hints(component_class.__init__)
|
|
114
|
+
except (NameError, TypeError):
|
|
115
|
+
hints = {}
|
|
116
|
+
props: dict[str, PropertySchema] = {prop.name: _copy_prop(prop) for prop in _COMMON_PROPS}
|
|
117
|
+
for parameter in signature.parameters.values():
|
|
118
|
+
if parameter.name in {"self", "children", "key", "props"} or parameter.kind not in {
|
|
119
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
120
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
121
|
+
}:
|
|
122
|
+
continue
|
|
123
|
+
wire_name = _wire_name(component_class, parameter.name)
|
|
124
|
+
props[wire_name] = _property_schema(wire_name, hints.get(parameter.name, parameter.annotation))
|
|
125
|
+
summary = inspect.cleandoc(component_class.__doc__) if component_class.__doc__ else None
|
|
126
|
+
ordered_props = list(props.values())
|
|
127
|
+
ordered_props.sort(key=lambda prop: prop.name)
|
|
128
|
+
return ComponentSchema(
|
|
129
|
+
package=package,
|
|
130
|
+
tag=component_class.tag,
|
|
131
|
+
class_name=name,
|
|
132
|
+
summary=summary.splitlines()[0] if summary else None,
|
|
133
|
+
props=ordered_props,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _wire_name(component_class: type[Component], parameter: str) -> str:
|
|
138
|
+
sentinel = f"__spaday_studio_{parameter}__"
|
|
139
|
+
try:
|
|
140
|
+
props = cast(Any, component_class)(**{parameter: sentinel}).to_node().get("props", {})
|
|
141
|
+
except (TypeError, ValueError):
|
|
142
|
+
return parameter.removesuffix("_")
|
|
143
|
+
tagged = {"Str": sentinel}
|
|
144
|
+
return next((name for name, value in props.items() if value == tagged), parameter.removesuffix("_"))
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _property_schema(name: str, annotation: object) -> PropertySchema:
|
|
148
|
+
args = tuple(argument for argument in get_args(annotation) if argument is not type(None))
|
|
149
|
+
if len(args) == 1:
|
|
150
|
+
annotation = args[0]
|
|
151
|
+
if get_origin(annotation) is Literal:
|
|
152
|
+
return PropertySchema(name=name, kind="enum", choices=list(get_args(annotation)))
|
|
153
|
+
if annotation is str:
|
|
154
|
+
return PropertySchema(name=name, kind="string")
|
|
155
|
+
if annotation is bool:
|
|
156
|
+
return PropertySchema(name=name, kind="boolean")
|
|
157
|
+
if annotation in {int, float}:
|
|
158
|
+
return PropertySchema(name=name, kind="number")
|
|
159
|
+
return PropertySchema(name=name, kind="json")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _html_components() -> list[ComponentSchema]:
|
|
163
|
+
text_tags = ("a", "button", "h1", "h2", "h3", "label", "p", "span", "strong")
|
|
164
|
+
container_tags = ("article", "aside", "div", "footer", "header", "main", "nav", "section")
|
|
165
|
+
components: list[ComponentSchema] = [_html_schema(tag, [_TEXT_PROP]) for tag in text_tags]
|
|
166
|
+
components.extend(_html_schema(tag) for tag in container_tags)
|
|
167
|
+
components.extend(
|
|
168
|
+
(
|
|
169
|
+
_html_schema(
|
|
170
|
+
"button",
|
|
171
|
+
[
|
|
172
|
+
_TEXT_PROP,
|
|
173
|
+
PropertySchema(name="disabled", kind="boolean"),
|
|
174
|
+
PropertySchema(name="type", kind="enum", choices=["button", "submit", "reset"]),
|
|
175
|
+
],
|
|
176
|
+
),
|
|
177
|
+
_html_schema(
|
|
178
|
+
"input",
|
|
179
|
+
[
|
|
180
|
+
PropertySchema(name="checked", kind="boolean"),
|
|
181
|
+
PropertySchema(name="disabled", kind="boolean"),
|
|
182
|
+
PropertySchema(name="type", kind="string"),
|
|
183
|
+
PropertySchema(name="value", kind="string"),
|
|
184
|
+
],
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
unique = {component.tag: component for component in components}
|
|
189
|
+
return list(unique.values())
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _html_schema(tag: str, props: list[PropertySchema] | None = None) -> ComponentSchema:
|
|
193
|
+
combined: dict[str, PropertySchema] = {prop.name: _copy_prop(prop) for prop in _COMMON_PROPS}
|
|
194
|
+
combined.update({prop.name: _copy_prop(prop) for prop in props or ()})
|
|
195
|
+
ordered_props = list(combined.values())
|
|
196
|
+
ordered_props.sort(key=lambda prop: prop.name)
|
|
197
|
+
return ComponentSchema(
|
|
198
|
+
package="html",
|
|
199
|
+
tag=tag,
|
|
200
|
+
class_name=tag,
|
|
201
|
+
props=ordered_props,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _copy_prop(prop: PropertySchema) -> PropertySchema:
|
|
206
|
+
return PropertySchema(name=prop.name, kind=prop.kind, choices=list(prop.choices))
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
__all__ = [
|
|
210
|
+
"ComponentCatalog",
|
|
211
|
+
"ComponentSchema",
|
|
212
|
+
"ComponentSummary",
|
|
213
|
+
"PropertySchema",
|
|
214
|
+
"discover_catalog",
|
|
215
|
+
]
|
spaday_studio/example.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Runnable Studio pilot document."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .models import StudioDocument, StudioNode
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def node(id: str, tag: str, *, props: dict | None = None, children: list[StudioNode] | None = None) -> StudioNode:
|
|
9
|
+
"""Build one compact example document node."""
|
|
10
|
+
return StudioNode(id=id, tag=tag, props=props or {}, slots={"default": children or []})
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
document = StudioDocument(
|
|
14
|
+
title="Orbit operations",
|
|
15
|
+
root=node(
|
|
16
|
+
"app",
|
|
17
|
+
"main",
|
|
18
|
+
props={
|
|
19
|
+
"style": (
|
|
20
|
+
"min-height: 100%; box-sizing: border-box; padding: 3.5rem; color: #edf4ff; "
|
|
21
|
+
"font-family: Inter, ui-sans-serif, system-ui; background: radial-gradient(circle at 20% 0%, #23345f, #0b1020 48%)"
|
|
22
|
+
)
|
|
23
|
+
},
|
|
24
|
+
children=[
|
|
25
|
+
node(
|
|
26
|
+
"eyebrow",
|
|
27
|
+
"p",
|
|
28
|
+
props={
|
|
29
|
+
"textContent": "LIVE MISSION CONTROL",
|
|
30
|
+
"style": "margin: 0 0 0.75rem; color: #7dd3fc; font-size: 0.75rem; font-weight: 800; letter-spacing: 0.18em",
|
|
31
|
+
},
|
|
32
|
+
),
|
|
33
|
+
node(
|
|
34
|
+
"headline",
|
|
35
|
+
"h1",
|
|
36
|
+
props={
|
|
37
|
+
"textContent": "Build the interface while it is running.",
|
|
38
|
+
"style": "max-width: 760px; margin: 0; font-size: clamp(2.5rem, 6vw, 5.5rem); line-height: 0.96; letter-spacing: -0.055em",
|
|
39
|
+
},
|
|
40
|
+
),
|
|
41
|
+
node(
|
|
42
|
+
"intro",
|
|
43
|
+
"p",
|
|
44
|
+
props={
|
|
45
|
+
"textContent": "Select any element, edit its properties, and watch spaday patch the live component tree without remounting the page.",
|
|
46
|
+
"style": "max-width: 680px; margin: 1.5rem 0 2.5rem; color: #a9b8d4; font-size: 1.05rem; line-height: 1.7",
|
|
47
|
+
},
|
|
48
|
+
),
|
|
49
|
+
node(
|
|
50
|
+
"metrics",
|
|
51
|
+
"section",
|
|
52
|
+
props={"style": "display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; max-width: 920px"},
|
|
53
|
+
children=[
|
|
54
|
+
node(
|
|
55
|
+
"metric-latency",
|
|
56
|
+
"article",
|
|
57
|
+
props={
|
|
58
|
+
"textContent": "18 ms\nPATCH LATENCY",
|
|
59
|
+
"style": "white-space: pre-line; padding: 1.4rem; border: 1px solid #314366; border-radius: 18px; background: #111a30cc; font-size: 1.35rem; line-height: 1.7",
|
|
60
|
+
},
|
|
61
|
+
),
|
|
62
|
+
node(
|
|
63
|
+
"metric-revision",
|
|
64
|
+
"article",
|
|
65
|
+
props={
|
|
66
|
+
"textContent": "REV 0\nAUTHORITATIVE STATE",
|
|
67
|
+
"style": "white-space: pre-line; padding: 1.4rem; border: 1px solid #314366; border-radius: 18px; background: #111a30cc; font-size: 1.35rem; line-height: 1.7",
|
|
68
|
+
},
|
|
69
|
+
),
|
|
70
|
+
node(
|
|
71
|
+
"metric-agents",
|
|
72
|
+
"article",
|
|
73
|
+
props={
|
|
74
|
+
"textContent": "MCP\nAGENT READY",
|
|
75
|
+
"style": "white-space: pre-line; padding: 1.4rem; border: 1px solid #314366; border-radius: 18px; background: #111a30cc; font-size: 1.35rem; line-height: 1.7",
|
|
76
|
+
},
|
|
77
|
+
),
|
|
78
|
+
],
|
|
79
|
+
),
|
|
80
|
+
],
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
import uvicorn
|
|
87
|
+
|
|
88
|
+
from .server import create_app
|
|
89
|
+
|
|
90
|
+
uvicorn.run(create_app(document), host="127.0.0.1", port=8020)
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function q(s,i){let u=Object.fromEntries(Object.entries({...s.props,"data-spaday-studio-id":s.id}).map(([m,f])=>[m,i(f)])),p=Object.fromEntries(Object.entries(s.slots).map(([m,f])=>[m,f.map(h=>q(h,i))]));return{tag:s.tag,key:s.key??s.id,...Object.keys(u).length?{props:u}:{},...Object.keys(p).length?{slots:p}:{}}}function C(s,i){if(s.id===i)return s;for(let u of Object.values(s.slots))for(let p of u){let m=C(p,i);if(m)return m}}function j(s,i){for(let[u,p]of Object.entries(s.slots)){let m=p.findIndex(f=>f.id===i);if(m>=0)return{parent:s,slot:u,index:m};for(let f of p){let h=j(f,i);if(h)return h}}}function A(s,i,u=0,p=[]){p.push({id:s.id,label:`${s.tag} ${s.id}`,className:i===s.id?"studio-tree-selected":"",style:`padding-left: ${8+u*14}px`});for(let m of Object.values(s.slots))for(let f of m)A(f,i,u+1,p);return p}function d(s){let i=document.querySelector(s);if(!i)throw new Error(`Studio element ${s} is missing`);return i}function se({runtime:s,transport:i}){let u=d("#canvas"),p=d("#component-tree"),m=d("#inspector-form"),f=d("#selection-empty"),h=d("#component-label"),B=d("#component-summary"),M=d("#property-fields"),H=d("#studio-message"),W=d("#revision-status"),$=d("#preview-status"),F=d("#connection-status"),N=d("#component-type"),J=d("#add-component"),K=d("#catalog-note"),V=d("#move-up"),I=d("#remove-component"),g,y,_,w,r,v,R=new s.Store({treeItems:[]}),T=e=>({compute:{expr:"item",path:e},mode:"one-way"}),U=s.mount(p,{tag:"div",props:{className:i.toValue("studio-tree-list")},slots:{default:[{tag:"spa-each",props:{itemKey:i.toValue("id"),style:i.toValue("display:contents")},bindings:{items:{field:"treeItems",mode:"one-way"}},slots:{default:[{tag:"button",props:{type:i.toValue("button")},bindings:{textContent:T("label"),className:T("className"),style:T("style"),"data-studio-tree-id":T("id")}}]}}]}},R),S=()=>g?.preview??g?.document,k=(e,n=!1)=>{H.textContent=e,H.classList.toggle("studio-error",n)},O=e=>y?.components.find(n=>n.tag===e),E=async e=>{if(!g)return!1;if(g.preview)return k("Discard or commit the active MCP preview before editing in the inspector.",!0),!1;let n=await fetch("/api/operations",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({expected_revision:g.revision,operations:e})}),t=await n.json();return n.ok?(k("Edit accepted; waiting for the authoritative transports patch."),!0):(k(t.error??"Edit failed",!0),!1)},z=(e,n)=>({name:e,kind:typeof n=="boolean"?"boolean":typeof n=="number"?"number":typeof n=="string"?"string":"json",choices:[]}),G=(e,n)=>{let t;if(e.kind==="boolean"||e.kind==="enum"){let o=document.createElement("select");o.append(new Option("Not set",""));let l=e.kind==="boolean"?[!0,!1]:e.choices;for(let a of l)o.append(new Option(String(a),JSON.stringify(a)));if(n!==void 0){let a=JSON.stringify(n);[...o.options].some(x=>x.value===a)||o.append(new Option(String(n),a)),o.value=a}t=o}else if(e.kind==="json"||e.name==="style"||e.name==="textContent"){let o=document.createElement("textarea");o.rows=e.name==="style"?5:3,o.value=n===void 0?"":e.kind==="json"?JSON.stringify(n,null,2):String(n),t=o}else{let o=document.createElement("input");o.type=e.kind==="number"?"number":"text",o.value=n===void 0?"":String(n),t=o}t.className="studio-property-control",t.dataset.studioProp=e.name,t.dataset.kind=e.kind,t.dataset.present=String(n!==void 0),t.dataset.dirty="false";let c=()=>{t.dataset.dirty="true"};return t.addEventListener("input",c),t.addEventListener("change",c),t},Q=e=>{let n=O(e.tag);B.textContent=n?.summary??(n?`${n.package} component`:"No selected catalog schema; showing authored properties only.");let t=new Map((n?.props??[]).map(o=>[o.name,o]));for(let[o,l]of Object.entries(e.props))t.has(o)||t.set(o,z(o,l));Object.values(e.slots).some(o=>o.length>0)&&!Object.prototype.hasOwnProperty.call(e.props,"textContent")&&t.delete("textContent"),M.replaceChildren();for(let o of[...t.values()].sort((l,a)=>l.name.localeCompare(a.name))){let l=document.createElement("div");l.className="studio-property-field";let a=document.createElement("div");a.className="studio-property-heading";let x=document.createElement("strong");x.textContent=o.name;let D=document.createElement("code");D.textContent=o.kind,a.append(x,D);let oe=e.props[o.name];if(Object.prototype.hasOwnProperty.call(e.props,o.name)){let L=document.createElement("button");L.type="button",L.textContent="Unset",L.addEventListener("click",()=>{E([{kind:"unset_prop",id:e.id,name:o.name}])}),a.append(L)}l.append(a,G(o,oe)),M.append(l)}},X=e=>{let n=e.dataset.kind;if(n==="string")return e.value;if(e.value){if(n==="number"){let t=Number(e.value);if(!Number.isFinite(t))throw new Error("Enter a finite number.");return t}return JSON.parse(e.value)}},Y=()=>{if(!y)return;N.replaceChildren();let e=new Map;for(let t of y.components){let c=e.get(t.package)??[];c.push(t),e.set(t.package,c)}for(let[t,c]of e){let o=document.createElement("optgroup");o.label=t;for(let l of c)o.append(new Option(`${l.class_name} \xB7 <${l.tag}>`,l.tag));N.append(o)}O("p")&&(N.value="p");let n=y.available_packages.filter(t=>!y.selected_packages.includes(t));K.textContent=n.length?`Also installed: ${n.join(", ")}. Select with --package NAME.`:`${y.components.length} components available.`},b=e=>{let n=S();if(!n)return;let t=C(n.root,e);if(!t)return;r=e,u.querySelectorAll(".spaday-studio-selected").forEach(o=>o.classList.remove("spaday-studio-selected")),u.querySelector(`[data-spaday-studio-id="${CSS.escape(e)}"]`)?.classList.add("spaday-studio-selected"),f.hidden=!0,m.hidden=!1,h.value=`${t.tag} \xB7 ${t.id}`,Q(t),J.disabled=Object.prototype.hasOwnProperty.call(t.props,"textContent"),I.disabled=t.id===n.root.id;let c=j(n.root,t.id);V.disabled=!c||c.index===0,Z()},Z=()=>{let e=S();e&&R.set("treeItems",A(e.root,r))};p.addEventListener("click",e=>{let t=(e.target instanceof Element?e.target.closest("[data-studio-tree-id]"):null)?.dataset.studioTreeId;t&&b(t)});let ee=()=>{let e=S();if(!e||!g)return;let n=q(e.root,i.toValue);if(!w)w=s.mount(u,n);else{let t=JSON.parse(s.diff(JSON.stringify(_),JSON.stringify(n)));w=s.applyPatch(w,t)}_=n,W.textContent=`Revision ${g.revision}`,$.textContent=g.preview?"Preview draft":"Canonical",$.classList.toggle("studio-preview-active",!!g.preview),F.textContent="Live",v&&C(e.root,v)&&(r=v,v=void 0),r&&C(e.root,r)?b(r):b(e.root.id)};m.addEventListener("submit",e=>{if(e.preventDefault(),!r)return;let n=[];try{for(let t of M.querySelectorAll(".studio-property-control")){if(t.dataset.dirty!=="true")continue;let c=t.dataset.studioProp,o=X(t);o===void 0?t.dataset.present==="true"&&n.push({kind:"unset_prop",id:r,name:c}):n.push({kind:"set_prop",id:r,name:c,value:o})}}catch(t){k(t instanceof Error?t.message:"Invalid property value",!0);return}if(!n.length){k("No property changes to apply.");return}E(n)}),J.addEventListener("click",()=>{if(!r)return;let n=C(S().root,r)?.slots.default?.length??0,t=O(N.value);if(!t)return;let c=globalThis.crypto?.randomUUID?.()??Date.now().toString(36),o=`${t.tag.replace(/[^a-z0-9]+/g,"-")}-${c}`,l=t.props.some(a=>a.name==="textContent")?{textContent:`New ${t.class_name}`}:{};v=o,E([{kind:"insert",parent_id:r,slot:"default",index:n,node:{id:o,tag:t.tag,props:l,slots:{}}}]).then(a=>{a||(v=void 0)})}),V.addEventListener("click",()=>{if(!r)return;let e=j(S().root,r);!e||e.index===0||E([{kind:"move",id:r,parent_id:e.parent.id,slot:e.slot,index:e.index-1}])}),I.addEventListener("click",()=>{r&&E([{kind:"remove",id:r}])}),u.addEventListener("click",e=>{let n=e.target instanceof Element?e.target.closest("[data-spaday-studio-id]"):null;if(!n)return;e.preventDefault(),e.stopPropagation();let t=n.dataset.spadayStudioId;t&&b(t)},!0),fetch("/api/catalog").then(async e=>{if(!e.ok)throw new Error("Component catalog failed to load.");y=await e.json(),Y(),r&&b(r)}).catch(e=>{k(e instanceof Error?e.message:"Component catalog failed to load.",!0)});let P=new i.Client;P.onChange(e=>{g=i.fromValue(P.value(e.id)),ee()});let te=location.protocol==="https:"?"wss":"ws",ne=P.run(`${te}://${location.host}/ws`);return{stop(){ne.stop(),w?.remove(),U.remove()}}}export{q as compileNode,se as connectStudio};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ts/index.ts"],
|
|
4
|
+
"sourcesContent": ["type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport interface StudioNode {\n id: string;\n tag: string;\n key?: string | null;\n props: Record<string, JsonValue>;\n slots: Record<string, StudioNode[]>;\n}\n\ninterface StudioDocument {\n title: string;\n root: StudioNode;\n}\n\ninterface StudioState {\n revision: number;\n document: StudioDocument;\n preview: StudioDocument | null;\n preview_id: string | null;\n}\n\ntype PropertyKind = \"string\" | \"boolean\" | \"number\" | \"enum\" | \"json\";\n\ninterface PropertySchema {\n name: string;\n kind: PropertyKind;\n choices: JsonValue[];\n}\n\ninterface ComponentSchema {\n package: string;\n tag: string;\n class_name: string;\n summary?: string | null;\n props: PropertySchema[];\n}\n\ninterface ComponentCatalog {\n available_packages: string[];\n selected_packages: string[];\n components: ComponentSchema[];\n}\n\ntype PropertyControl =\n | HTMLInputElement\n | HTMLSelectElement\n | HTMLTextAreaElement;\n\ninterface WireNode {\n tag: string;\n key?: string;\n props?: Record<string, unknown>;\n bindings?: Record<string, unknown>;\n slots?: Record<string, WireNode[]>;\n}\n\ninterface RuntimeStore {\n set(field: string, value: unknown): void;\n}\n\ninterface RuntimeModule {\n Store: new (initial?: Record<string, unknown>) => RuntimeStore;\n mount(container: Element, tree: WireNode, store?: RuntimeStore): Element;\n diff(oldTree: string, newTree: string): string;\n applyPatch(root: Element, patch: unknown): Element;\n}\n\ninterface ClientMirror {\n onChange(listener: (change: { id: number }) => void): () => void;\n value(id: number): unknown;\n run(url: string, options?: { onMessage?: () => void }): { stop(): void };\n}\n\ninterface TransportModule {\n Client: new () => ClientMirror;\n fromValue(value: unknown): unknown;\n toValue(value: unknown): unknown;\n}\n\ninterface ConnectOptions {\n runtime: RuntimeModule;\n transport: TransportModule;\n}\n\ninterface TreeItem {\n id: string;\n label: string;\n className: string;\n style: string;\n}\n\nexport function compileNode(\n node: StudioNode,\n toValue: (value: unknown) => unknown,\n): WireNode {\n const props = Object.fromEntries(\n Object.entries({ ...node.props, \"data-spaday-studio-id\": node.id }).map(\n ([name, value]) => [name, toValue(value)],\n ),\n );\n const slots = Object.fromEntries(\n Object.entries(node.slots).map(([name, children]) => [\n name,\n children.map((child) => compileNode(child, toValue)),\n ]),\n );\n return {\n tag: node.tag,\n key: node.key ?? node.id,\n ...(Object.keys(props).length ? { props } : {}),\n ...(Object.keys(slots).length ? { slots } : {}),\n };\n}\n\nfunction findNode(node: StudioNode, id: string): StudioNode | undefined {\n if (node.id === id) return node;\n for (const children of Object.values(node.slots)) {\n for (const child of children) {\n const found = findNode(child, id);\n if (found) return found;\n }\n }\n return undefined;\n}\n\nfunction findLocation(\n node: StudioNode,\n id: string,\n): { parent: StudioNode; slot: string; index: number } | undefined {\n for (const [slot, children] of Object.entries(node.slots)) {\n const index = children.findIndex((child) => child.id === id);\n if (index >= 0) return { parent: node, slot, index };\n for (const child of children) {\n const found = findLocation(child, id);\n if (found) return found;\n }\n }\n return undefined;\n}\n\nfunction flattenTree(\n node: StudioNode,\n selectedId: string | undefined,\n depth = 0,\n items: TreeItem[] = [],\n): TreeItem[] {\n items.push({\n id: node.id,\n label: `${node.tag} ${node.id}`,\n className: selectedId === node.id ? \"studio-tree-selected\" : \"\",\n style: `padding-left: ${8 + depth * 14}px`,\n });\n for (const children of Object.values(node.slots)) {\n for (const child of children) {\n flattenTree(child, selectedId, depth + 1, items);\n }\n }\n return items;\n}\n\nfunction requiredElement<T extends Element>(selector: string): T {\n const element = document.querySelector<T>(selector);\n if (!element) throw new Error(`Studio element ${selector} is missing`);\n return element;\n}\n\nexport function connectStudio({ runtime, transport }: ConnectOptions): {\n stop(): void;\n} {\n const canvas = requiredElement<HTMLElement>(\"#canvas\");\n const tree = requiredElement<HTMLElement>(\"#component-tree\");\n const form = requiredElement<HTMLFormElement>(\"#inspector-form\");\n const empty = requiredElement<HTMLElement>(\"#selection-empty\");\n const label = requiredElement<HTMLInputElement>(\"#component-label\");\n const summary = requiredElement<HTMLElement>(\"#component-summary\");\n const propertyFields = requiredElement<HTMLElement>(\"#property-fields\");\n const message = requiredElement<HTMLElement>(\"#studio-message\");\n const revision = requiredElement<HTMLElement>(\"#revision-status\");\n const preview = requiredElement<HTMLElement>(\"#preview-status\");\n const connection = requiredElement<HTMLElement>(\"#connection-status\");\n const componentType = requiredElement<HTMLSelectElement>(\"#component-type\");\n const addComponent = requiredElement<HTMLButtonElement>(\"#add-component\");\n const catalogNote = requiredElement<HTMLElement>(\"#catalog-note\");\n const moveUp = requiredElement<HTMLButtonElement>(\"#move-up\");\n const remove = requiredElement<HTMLButtonElement>(\"#remove-component\");\n\n let state: StudioState | undefined;\n let catalog: ComponentCatalog | undefined;\n let currentTree: WireNode | undefined;\n let root: Element | undefined;\n let selectedId: string | undefined;\n let pendingSelection: string | undefined;\n\n const treeStore = new runtime.Store({ treeItems: [] });\n const itemBinding = (path: keyof TreeItem) => ({\n compute: { expr: \"item\", path },\n mode: \"one-way\",\n });\n const treeRoot = runtime.mount(\n tree,\n {\n tag: \"div\",\n props: { className: transport.toValue(\"studio-tree-list\") },\n slots: {\n default: [\n {\n tag: \"spa-each\",\n props: {\n itemKey: transport.toValue(\"id\"),\n style: transport.toValue(\"display:contents\"),\n },\n bindings: { items: { field: \"treeItems\", mode: \"one-way\" } },\n slots: {\n default: [\n {\n tag: \"button\",\n props: { type: transport.toValue(\"button\") },\n bindings: {\n textContent: itemBinding(\"label\"),\n className: itemBinding(\"className\"),\n style: itemBinding(\"style\"),\n \"data-studio-tree-id\": itemBinding(\"id\"),\n },\n },\n ],\n },\n },\n ],\n },\n },\n treeStore,\n );\n\n const activeDocument = (): StudioDocument | undefined =>\n state?.preview ?? state?.document;\n\n const showMessage = (value: string, error = false) => {\n message.textContent = value;\n message.classList.toggle(\"studio-error\", error);\n };\n\n const schemaFor = (tag: string): ComponentSchema | undefined =>\n catalog?.components.find((component) => component.tag === tag);\n\n const postOperations = async (operations: unknown[]): Promise<boolean> => {\n if (!state) return false;\n if (state.preview) {\n showMessage(\n \"Discard or commit the active MCP preview before editing in the inspector.\",\n true,\n );\n return false;\n }\n const response = await fetch(\"/api/operations\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ expected_revision: state.revision, operations }),\n });\n const result = (await response.json()) as { error?: string };\n if (!response.ok) {\n showMessage(result.error ?? \"Edit failed\", true);\n return false;\n }\n showMessage(\n \"Edit accepted; waiting for the authoritative transports patch.\",\n );\n return true;\n };\n\n const inferredProperty = (\n name: string,\n value: JsonValue,\n ): PropertySchema => ({\n name,\n kind:\n typeof value === \"boolean\"\n ? \"boolean\"\n : typeof value === \"number\"\n ? \"number\"\n : typeof value === \"string\"\n ? \"string\"\n : \"json\",\n choices: [],\n });\n\n const propertyControl = (\n property: PropertySchema,\n value: JsonValue | undefined,\n ): PropertyControl => {\n let control: PropertyControl;\n if (property.kind === \"boolean\" || property.kind === \"enum\") {\n const select = document.createElement(\"select\");\n select.append(new Option(\"Not set\", \"\"));\n const choices =\n property.kind === \"boolean\" ? [true, false] : property.choices;\n for (const choice of choices) {\n select.append(new Option(String(choice), JSON.stringify(choice)));\n }\n if (value !== undefined) {\n const encoded = JSON.stringify(value);\n if (![...select.options].some((option) => option.value === encoded)) {\n select.append(new Option(String(value), encoded));\n }\n select.value = encoded;\n }\n control = select;\n } else if (\n property.kind === \"json\" ||\n property.name === \"style\" ||\n property.name === \"textContent\"\n ) {\n const textarea = document.createElement(\"textarea\");\n textarea.rows = property.name === \"style\" ? 5 : 3;\n textarea.value =\n value === undefined\n ? \"\"\n : property.kind === \"json\"\n ? JSON.stringify(value, null, 2)\n : String(value);\n control = textarea;\n } else {\n const input = document.createElement(\"input\");\n input.type = property.kind === \"number\" ? \"number\" : \"text\";\n input.value = value === undefined ? \"\" : String(value);\n control = input;\n }\n control.className = \"studio-property-control\";\n control.dataset.studioProp = property.name;\n control.dataset.kind = property.kind;\n control.dataset.present = String(value !== undefined);\n control.dataset.dirty = \"false\";\n const markDirty = () => {\n control.dataset.dirty = \"true\";\n };\n control.addEventListener(\"input\", markDirty);\n control.addEventListener(\"change\", markDirty);\n return control;\n };\n\n const renderProperties = (node: StudioNode) => {\n const component = schemaFor(node.tag);\n summary.textContent =\n component?.summary ??\n (component\n ? `${component.package} component`\n : \"No selected catalog schema; showing authored properties only.\");\n const properties = new Map(\n (component?.props ?? []).map((property) => [property.name, property]),\n );\n for (const [name, value] of Object.entries(node.props)) {\n if (!properties.has(name))\n properties.set(name, inferredProperty(name, value));\n }\n const hasChildren = Object.values(node.slots).some(\n (children) => children.length > 0,\n );\n if (\n hasChildren &&\n !Object.prototype.hasOwnProperty.call(node.props, \"textContent\")\n ) {\n properties.delete(\"textContent\");\n }\n propertyFields.replaceChildren();\n for (const property of [...properties.values()].sort((a, b) =>\n a.name.localeCompare(b.name),\n )) {\n const field = document.createElement(\"div\");\n field.className = \"studio-property-field\";\n const heading = document.createElement(\"div\");\n heading.className = \"studio-property-heading\";\n const name = document.createElement(\"strong\");\n name.textContent = property.name;\n const kind = document.createElement(\"code\");\n kind.textContent = property.kind;\n heading.append(name, kind);\n const value = node.props[property.name];\n if (Object.prototype.hasOwnProperty.call(node.props, property.name)) {\n const unset = document.createElement(\"button\");\n unset.type = \"button\";\n unset.textContent = \"Unset\";\n unset.addEventListener(\"click\", () => {\n void postOperations([\n { kind: \"unset_prop\", id: node.id, name: property.name },\n ]);\n });\n heading.append(unset);\n }\n field.append(heading, propertyControl(property, value));\n propertyFields.append(field);\n }\n };\n\n const readControl = (control: PropertyControl): JsonValue | undefined => {\n const kind = control.dataset.kind as PropertyKind;\n if (kind === \"string\") return control.value;\n if (!control.value) return undefined;\n if (kind === \"number\") {\n const value = Number(control.value);\n if (!Number.isFinite(value)) throw new Error(\"Enter a finite number.\");\n return value;\n }\n return JSON.parse(control.value) as JsonValue;\n };\n\n const renderComponentOptions = () => {\n if (!catalog) return;\n componentType.replaceChildren();\n const packages = new Map<string, ComponentSchema[]>();\n for (const component of catalog.components) {\n const components = packages.get(component.package) ?? [];\n components.push(component);\n packages.set(component.package, components);\n }\n for (const [packageName, components] of packages) {\n const group = document.createElement(\"optgroup\");\n group.label = packageName;\n for (const component of components) {\n group.append(\n new Option(\n `${component.class_name} \u00B7 <${component.tag}>`,\n component.tag,\n ),\n );\n }\n componentType.append(group);\n }\n if (schemaFor(\"p\")) componentType.value = \"p\";\n const inactive = catalog.available_packages.filter(\n (name) => !catalog!.selected_packages.includes(name),\n );\n catalogNote.textContent = inactive.length\n ? `Also installed: ${inactive.join(\", \")}. Select with --package NAME.`\n : `${catalog.components.length} components available.`;\n };\n\n const select = (id: string) => {\n const active = activeDocument();\n if (!active) return;\n const selected = findNode(active.root, id);\n if (!selected) return;\n selectedId = id;\n canvas\n .querySelectorAll(\".spaday-studio-selected\")\n .forEach((element) => element.classList.remove(\"spaday-studio-selected\"));\n canvas\n .querySelector<HTMLElement>(`[data-spaday-studio-id=\"${CSS.escape(id)}\"]`)\n ?.classList.add(\"spaday-studio-selected\");\n empty.hidden = true;\n form.hidden = false;\n label.value = `${selected.tag} \u00B7 ${selected.id}`;\n renderProperties(selected);\n addComponent.disabled = Object.prototype.hasOwnProperty.call(\n selected.props,\n \"textContent\",\n );\n remove.disabled = selected.id === active.root.id;\n const location = findLocation(active.root, selected.id);\n moveUp.disabled = !location || location.index === 0;\n renderTree();\n };\n\n const renderTree = () => {\n const active = activeDocument();\n if (!active) return;\n treeStore.set(\"treeItems\", flattenTree(active.root, selectedId));\n };\n\n tree.addEventListener(\"click\", (event) => {\n const target =\n event.target instanceof Element\n ? event.target.closest<HTMLElement>(\"[data-studio-tree-id]\")\n : null;\n const id = target?.dataset.studioTreeId;\n if (id) select(id);\n });\n\n const render = () => {\n const active = activeDocument();\n if (!active || !state) return;\n const nextTree = compileNode(active.root, transport.toValue);\n if (!root) root = runtime.mount(canvas, nextTree);\n else {\n const patch = JSON.parse(\n runtime.diff(JSON.stringify(currentTree), JSON.stringify(nextTree)),\n ) as unknown;\n root = runtime.applyPatch(root, patch);\n }\n currentTree = nextTree;\n revision.textContent = `Revision ${state.revision}`;\n preview.textContent = state.preview ? \"Preview draft\" : \"Canonical\";\n preview.classList.toggle(\"studio-preview-active\", Boolean(state.preview));\n connection.textContent = \"Live\";\n if (pendingSelection && findNode(active.root, pendingSelection)) {\n selectedId = pendingSelection;\n pendingSelection = undefined;\n }\n if (selectedId && findNode(active.root, selectedId)) select(selectedId);\n else select(active.root.id);\n };\n\n form.addEventListener(\"submit\", (event) => {\n event.preventDefault();\n if (!selectedId) return;\n const operations: unknown[] = [];\n try {\n for (const control of propertyFields.querySelectorAll<PropertyControl>(\n \".studio-property-control\",\n )) {\n if (control.dataset.dirty !== \"true\") continue;\n const name = control.dataset.studioProp!;\n const value = readControl(control);\n if (value === undefined) {\n if (control.dataset.present === \"true\") {\n operations.push({ kind: \"unset_prop\", id: selectedId, name });\n }\n } else {\n operations.push({ kind: \"set_prop\", id: selectedId, name, value });\n }\n }\n } catch (error) {\n showMessage(\n error instanceof Error ? error.message : \"Invalid property value\",\n true,\n );\n return;\n }\n if (!operations.length) {\n showMessage(\"No property changes to apply.\");\n return;\n }\n void postOperations(operations);\n });\n\n addComponent.addEventListener(\"click\", () => {\n if (!selectedId) return;\n const parent = findNode(activeDocument()!.root, selectedId);\n const index = parent?.slots.default?.length ?? 0;\n const component = schemaFor(componentType.value);\n if (!component) return;\n const suffix = globalThis.crypto?.randomUUID?.() ?? Date.now().toString(36);\n const id = `${component.tag.replace(/[^a-z0-9]+/g, \"-\")}-${suffix}`;\n const props = component.props.some(\n (property) => property.name === \"textContent\",\n )\n ? { textContent: `New ${component.class_name}` }\n : {};\n pendingSelection = id;\n void postOperations([\n {\n kind: \"insert\",\n parent_id: selectedId,\n slot: \"default\",\n index,\n node: {\n id,\n tag: component.tag,\n props,\n slots: {},\n },\n },\n ]).then((accepted) => {\n if (!accepted) pendingSelection = undefined;\n });\n });\n\n moveUp.addEventListener(\"click\", () => {\n if (!selectedId) return;\n const location = findLocation(activeDocument()!.root, selectedId);\n if (!location || location.index === 0) return;\n void postOperations([\n {\n kind: \"move\",\n id: selectedId,\n parent_id: location.parent.id,\n slot: location.slot,\n index: location.index - 1,\n },\n ]);\n });\n\n remove.addEventListener(\"click\", () => {\n if (selectedId) void postOperations([{ kind: \"remove\", id: selectedId }]);\n });\n\n canvas.addEventListener(\n \"click\",\n (event) => {\n const target =\n event.target instanceof Element\n ? event.target.closest<HTMLElement>(\"[data-spaday-studio-id]\")\n : null;\n if (!target) return;\n event.preventDefault();\n event.stopPropagation();\n const id = target.dataset.spadayStudioId;\n if (id) select(id);\n },\n true,\n );\n\n void fetch(\"/api/catalog\")\n .then(async (response) => {\n if (!response.ok) throw new Error(\"Component catalog failed to load.\");\n catalog = (await response.json()) as ComponentCatalog;\n renderComponentOptions();\n if (selectedId) select(selectedId);\n })\n .catch((error: unknown) => {\n showMessage(\n error instanceof Error\n ? error.message\n : \"Component catalog failed to load.\",\n true,\n );\n });\n\n const client = new transport.Client();\n client.onChange((change) => {\n state = transport.fromValue(client.value(change.id)) as StudioState;\n render();\n });\n const scheme = location.protocol === \"https:\" ? \"wss\" : \"ws\";\n const link = client.run(`${scheme}://${location.host}/ws`);\n\n return {\n stop() {\n link.stop();\n root?.remove();\n treeRoot.remove();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["compileNode", "node", "toValue", "props", "name", "value", "slots", "children", "child", "findNode", "id", "found", "findLocation", "slot", "index", "flattenTree", "selectedId", "depth", "items", "requiredElement", "selector", "element", "connectStudio", "runtime", "transport", "canvas", "tree", "form", "empty", "label", "summary", "propertyFields", "message", "revision", "preview", "connection", "componentType", "addComponent", "catalogNote", "moveUp", "remove", "state", "catalog", "currentTree", "root", "pendingSelection", "treeStore", "itemBinding", "path", "treeRoot", "activeDocument", "showMessage", "error", "schemaFor", "tag", "component", "postOperations", "operations", "response", "result", "inferredProperty", "propertyControl", "property", "control", "select", "choices", "choice", "encoded", "option", "textarea", "input", "markDirty", "renderProperties", "properties", "a", "b", "field", "heading", "kind", "unset", "readControl", "renderComponentOptions", "packages", "components", "packageName", "group", "inactive", "active", "selected", "location", "renderTree", "event", "render", "nextTree", "patch", "suffix", "accepted", "target", "client", "change", "scheme", "link"]
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{color:#152139;font-synthesis:none;background:#edf1f7;font-family:Inter,ui-sans-serif,system-ui,sans-serif}*{box-sizing:border-box}body{min-width:1080px;min-height:100vh;margin:0;overflow:hidden}button,input,textarea{color:inherit;font:inherit}.studio-header{color:#f8fbff;background:#10182b;border-bottom:1px solid #293653;justify-content:space-between;align-items:center;height:58px;padding:0 18px;display:flex}.studio-kicker{color:#8292ae;letter-spacing:.08em;text-transform:uppercase;margin-left:12px;font-size:.75rem}.studio-status{align-items:center;gap:8px;font-size:.78rem;display:flex}.studio-status span,.studio-canvas-toolbar span{border:1px solid #33415d;border-radius:999px;padding:5px 9px}.studio-export{color:#dbeafe;border:1px solid #486080;border-radius:7px;padding:5px 9px;font-size:.78rem;text-decoration:none}.studio-export:hover{color:#fff;background:#1d4f91}.studio-preview-active{color:#291900;background:#fbbf24;border-color:#fbbf24!important}.studio-shell{grid-template-columns:250px minmax(580px,1fr) 310px;height:calc(100vh - 58px);display:grid}.studio-panel{background:#f9fbfe;padding:20px 16px;overflow:auto}.studio-panel h2{letter-spacing:.06em;text-transform:uppercase;margin:0 0 6px;font-size:.88rem}.studio-panel>p{color:#69758a;margin:0 0 18px;font-size:.82rem;line-height:1.5}.studio-tree-panel{border-right:1px solid #d7deea}#component-tree .studio-tree-list{margin:0;padding:0}#component-tree button{text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;background:0 0;border:0;border-radius:7px;width:100%;margin:1px 0;padding:6px 8px;font-family:ui-monospace,SFMono-Regular,monospace;font-size:.74rem;overflow:hidden}#component-tree button:hover,#component-tree .studio-tree-selected{color:#125cc5;background:#e7f0ff}.studio-add-card{border-top:1px solid #d7deea;gap:9px;margin-top:18px;padding-top:16px;display:grid}.studio-add-card label{color:#526077;gap:6px;font-size:.74rem;font-weight:700;display:grid}.studio-add-card select,.studio-add-card button{width:100%;font:inherit;background:#fff;border:1px solid #c6d0df;border-radius:8px;padding:8px}.studio-add-card button{color:#fff;cursor:pointer;background:#1769d2;border-color:#1769d2}.studio-add-card button:disabled{cursor:not-allowed;opacity:.45}#catalog-note{color:#69758a;font-size:.7rem;line-height:1.45}.studio-canvas-frame{background:#dfe5ef;min-width:0;padding:14px}.studio-canvas-toolbar{color:#5f6c81;justify-content:space-between;align-items:center;height:38px;padding:0 10px;font-size:.72rem;display:flex}.studio-canvas{background:#fff;border-radius:10px;height:calc(100% - 38px);overflow:auto;box-shadow:0 12px 35px #1e2b4424}.spaday-studio-selected{outline-offset:-3px;outline:3px solid #38bdf8!important}.studio-inspector{border-left:1px solid #d7deea}.studio-inspector form{gap:13px;display:grid}#component-summary{color:#69758a;margin:-4px 0 0;font-size:.73rem;line-height:1.4}#property-fields{gap:12px;display:grid}.studio-property-field{gap:5px;display:grid}.studio-property-heading{align-items:center;gap:6px;min-width:0;display:flex}.studio-property-heading strong{color:#526077;text-overflow:ellipsis;font-size:.76rem;overflow:hidden}.studio-property-heading code{color:#8792a5;margin-right:auto;font-size:.65rem}.studio-inspector .studio-property-heading button{color:#9b2c25;border:0;padding:2px 5px;font-size:.65rem}.studio-inspector form[hidden]{display:none}.studio-inspector label{color:#526077;gap:5px;font-size:.76rem;font-weight:700;display:grid}.studio-inspector input,.studio-inspector textarea,.studio-inspector select{resize:vertical;background:#fff;border:1px solid #c8d1df;border-radius:8px;width:100%;padding:9px 10px;font-size:.8rem;font-weight:400;line-height:1.45}.studio-inspector button{cursor:pointer;background:#fff;border:1px solid #c6d0df;border-radius:8px;padding:8px 10px}.studio-inspector button:disabled{cursor:not-allowed;opacity:.45}.studio-inspector .studio-primary{color:#fff;background:#1769d2;border-color:#1769d2;font-weight:750}.studio-button-row{grid-template-columns:repeat(2,1fr);gap:6px;display:grid}.studio-button-row button{padding-inline:5px;font-size:.7rem}.studio-agent-card{color:#cad9ee;background:#17233a;border-radius:10px;gap:7px;margin-top:24px;padding:13px;font-size:.75rem;display:grid}.studio-agent-card code{color:#7dd3fc;text-overflow:ellipsis;overflow:hidden}#studio-message{color:#2e6d48;margin-top:14px}#studio-message.studio-error{color:#b42318}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset="UTF-8" />
|
|
4
|
+
<title>spaday-studio</title>
|
|
5
|
+
<meta name="description" content="AI-native visual development environment for spaday" />
|
|
6
|
+
<meta name="author" content="1kbgz" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
|
9
|
+
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
|
10
|
+
<script type="module" src="cdn/index.js"></script>
|
|
11
|
+
</head>
|
|
12
|
+
<body></body>
|
|
13
|
+
</html>
|
spaday_studio/index.html
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>spaday Studio</title>
|
|
7
|
+
<link rel="stylesheet" href="/components/studio/css/index.css" />
|
|
8
|
+
<!-- component-packages -->
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<header class="studio-header">
|
|
12
|
+
<div>
|
|
13
|
+
<strong>spaday Studio</strong>
|
|
14
|
+
<span class="studio-kicker">live application pilot</span>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="studio-status">
|
|
17
|
+
<a class="studio-export" href="/api/export/python" download="spaday_app.py">Export Python</a>
|
|
18
|
+
<span id="preview-status">Canonical</span>
|
|
19
|
+
<span id="revision-status">Connecting…</span>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
<main class="studio-shell">
|
|
23
|
+
<aside class="studio-panel studio-tree-panel">
|
|
24
|
+
<h2>Components</h2>
|
|
25
|
+
<p>Select a component in this tree or directly on the canvas.</p>
|
|
26
|
+
<div id="component-tree"></div>
|
|
27
|
+
<div class="studio-add-card">
|
|
28
|
+
<label>
|
|
29
|
+
Insert into selection
|
|
30
|
+
<select id="component-type"></select>
|
|
31
|
+
</label>
|
|
32
|
+
<button type="button" id="add-component">Add component</button>
|
|
33
|
+
<span id="catalog-note"></span>
|
|
34
|
+
</div>
|
|
35
|
+
</aside>
|
|
36
|
+
<section class="studio-canvas-frame">
|
|
37
|
+
<div class="studio-canvas-toolbar">
|
|
38
|
+
<span>Desktop · 1280 px</span>
|
|
39
|
+
<span id="connection-status">Offline</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div id="canvas" class="studio-canvas"></div>
|
|
42
|
+
</section>
|
|
43
|
+
<aside class="studio-panel studio-inspector">
|
|
44
|
+
<h2>Inspector</h2>
|
|
45
|
+
<p id="selection-empty">Select a component to edit its authored properties.</p>
|
|
46
|
+
<form id="inspector-form" hidden>
|
|
47
|
+
<label>Component <input id="component-label" readonly /></label>
|
|
48
|
+
<p id="component-summary"></p>
|
|
49
|
+
<div id="property-fields"></div>
|
|
50
|
+
<button type="submit" class="studio-primary">Apply properties</button>
|
|
51
|
+
<div class="studio-button-row">
|
|
52
|
+
<button type="button" id="move-up">Move up</button>
|
|
53
|
+
<button type="button" id="remove-component">Remove</button>
|
|
54
|
+
</div>
|
|
55
|
+
</form>
|
|
56
|
+
<div class="studio-agent-card">
|
|
57
|
+
<strong>Agent endpoint</strong>
|
|
58
|
+
<code>/mcp</code>
|
|
59
|
+
<span>Use MCP preview tools to stage a live draft before committing.</span>
|
|
60
|
+
</div>
|
|
61
|
+
<p id="studio-message" role="status"></p>
|
|
62
|
+
</aside>
|
|
63
|
+
</main>
|
|
64
|
+
<script type="module">
|
|
65
|
+
import { connectStudio } from "/components/studio/cdn/index.js";
|
|
66
|
+
import * as runtime from "/js/cdn/index.js";
|
|
67
|
+
import * as transport from "/js/transports/cdn/index.js";
|
|
68
|
+
|
|
69
|
+
await runtime.init({ module_or_path: "/js/pkg/spaday_bg.wasm" });
|
|
70
|
+
await transport.wasm.default({ module_or_path: "/js/transports/pkg/transports_bg.wasm" });
|
|
71
|
+
connectStudio({ runtime, transport });
|
|
72
|
+
</script>
|
|
73
|
+
</body>
|
|
74
|
+
</html>
|