@unoverse-platform/studio 0.1.56 → 0.1.57
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.
- package/local/sample-template/chat/components/assistant-turn.yaml +40 -0
- package/local/sample-template/chat/components/core-focus.yaml +46 -0
- package/local/sample-template/chat/components/core.yaml +41 -0
- package/local/sample-template/chat/components/focus-overlay.yaml +20 -0
- package/local/sample-template/chat/components/header.yaml +53 -0
- package/local/sample-template/chat/components/suggestions.yaml +142 -0
- package/local/sample-template/chat/components/thinking-dots.yaml +38 -0
- package/local/sample-template/chat/components/user-turn.yaml +22 -0
- package/local/sample-template/chat/layouts/focused.yaml +12 -0
- package/local/sample-template/chat/layouts/standard.yaml +9 -0
- package/local/sample-template/chat/manifest.yaml +41 -0
- package/local/sample-template/chat/states/conversation.yaml +21 -0
- package/local/sample-template/chat/states/welcome.yaml +119 -0
- package/local/scaffold.mjs +15 -2
- package/package.json +1 -1
- package/screens/TemplatesView.tsx +18 -3
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
visibleWhen: active
|
|
3
|
+
style:
|
|
4
|
+
direction: row
|
|
5
|
+
gap: "2"
|
|
6
|
+
align: start
|
|
7
|
+
children:
|
|
8
|
+
- type: Box
|
|
9
|
+
style:
|
|
10
|
+
direction: column
|
|
11
|
+
flex: 1
|
|
12
|
+
gap: "2"
|
|
13
|
+
minWidth: 0
|
|
14
|
+
children:
|
|
15
|
+
- type: ComponentSlot
|
|
16
|
+
select: {}
|
|
17
|
+
- type: Box
|
|
18
|
+
visibleWhen: streaming
|
|
19
|
+
children:
|
|
20
|
+
- type: Box
|
|
21
|
+
visibleWhen: empty
|
|
22
|
+
style:
|
|
23
|
+
direction: row
|
|
24
|
+
gap: "2"
|
|
25
|
+
align: center
|
|
26
|
+
children:
|
|
27
|
+
- $include: components/thinking-dots
|
|
28
|
+
- type: Text
|
|
29
|
+
bind:
|
|
30
|
+
value: thinkingText
|
|
31
|
+
style:
|
|
32
|
+
font: body.sm
|
|
33
|
+
color: text.secondary
|
|
34
|
+
- type: Text
|
|
35
|
+
visibleWhen: time
|
|
36
|
+
bind:
|
|
37
|
+
value: time
|
|
38
|
+
style:
|
|
39
|
+
font: body.sm
|
|
40
|
+
color: text.secondary
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# THE CHAT COLUMN WITH A FOCUSED COMPONENT OVER IT — core, plus the focus surface
|
|
2
|
+
# laid over the conversation region. A core VARIANT, not a copy: header, states and
|
|
3
|
+
# composer are the same shared files core.yaml includes; the only difference is the
|
|
4
|
+
# overlay slot.
|
|
5
|
+
type: Box
|
|
6
|
+
appWidth: chat
|
|
7
|
+
style:
|
|
8
|
+
direction: column
|
|
9
|
+
minHeight: "0"
|
|
10
|
+
children:
|
|
11
|
+
- type: Box
|
|
12
|
+
style:
|
|
13
|
+
direction: column
|
|
14
|
+
flex: 1
|
|
15
|
+
minHeight: "0"
|
|
16
|
+
position: relative
|
|
17
|
+
children:
|
|
18
|
+
- $include: components/header
|
|
19
|
+
- type: Box
|
|
20
|
+
style:
|
|
21
|
+
direction: column
|
|
22
|
+
flex: 1
|
|
23
|
+
minHeight: "0"
|
|
24
|
+
position: relative
|
|
25
|
+
when:
|
|
26
|
+
- field: hasMessages
|
|
27
|
+
eq: true
|
|
28
|
+
apply:
|
|
29
|
+
background: surface.base
|
|
30
|
+
radiusTopLeft: 2xl
|
|
31
|
+
overflow: hidden
|
|
32
|
+
children:
|
|
33
|
+
- $include: states/welcome
|
|
34
|
+
- $include: states/conversation
|
|
35
|
+
- type: Box
|
|
36
|
+
style:
|
|
37
|
+
position: absolute
|
|
38
|
+
bottom: "0"
|
|
39
|
+
left: "0"
|
|
40
|
+
right: "0"
|
|
41
|
+
direction: column
|
|
42
|
+
children:
|
|
43
|
+
- $include: components/suggestions
|
|
44
|
+
- $include: components/focus-overlay
|
|
45
|
+
- type: Ref
|
|
46
|
+
ref: composer-bar
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
appWidth: chat
|
|
3
|
+
style:
|
|
4
|
+
direction: column
|
|
5
|
+
minHeight: "0"
|
|
6
|
+
children:
|
|
7
|
+
- type: Box
|
|
8
|
+
style:
|
|
9
|
+
direction: column
|
|
10
|
+
flex: 1
|
|
11
|
+
minHeight: "0"
|
|
12
|
+
position: relative
|
|
13
|
+
children:
|
|
14
|
+
- $include: components/header
|
|
15
|
+
- type: Box
|
|
16
|
+
style:
|
|
17
|
+
direction: column
|
|
18
|
+
flex: 1
|
|
19
|
+
minHeight: "0"
|
|
20
|
+
position: relative
|
|
21
|
+
when:
|
|
22
|
+
- field: hasMessages
|
|
23
|
+
eq: true
|
|
24
|
+
apply:
|
|
25
|
+
background: surface.base
|
|
26
|
+
radiusTopLeft: 2xl
|
|
27
|
+
overflow: hidden
|
|
28
|
+
children:
|
|
29
|
+
- $include: states/welcome
|
|
30
|
+
- $include: states/conversation
|
|
31
|
+
- type: Box
|
|
32
|
+
style:
|
|
33
|
+
position: absolute
|
|
34
|
+
bottom: "0"
|
|
35
|
+
left: "0"
|
|
36
|
+
right: "0"
|
|
37
|
+
direction: column
|
|
38
|
+
children:
|
|
39
|
+
- $include: components/suggestions
|
|
40
|
+
- type: Ref
|
|
41
|
+
ref: composer-bar
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type: ComponentSlot
|
|
2
|
+
select:
|
|
3
|
+
from: all
|
|
4
|
+
where:
|
|
5
|
+
field: view
|
|
6
|
+
eq: focused
|
|
7
|
+
limit: 1
|
|
8
|
+
frame:
|
|
9
|
+
type: Box
|
|
10
|
+
style:
|
|
11
|
+
position: absolute
|
|
12
|
+
inset: "0"
|
|
13
|
+
width: full
|
|
14
|
+
height: full
|
|
15
|
+
background: surface.base
|
|
16
|
+
overflow: hidden
|
|
17
|
+
direction: column
|
|
18
|
+
align: stretch
|
|
19
|
+
children:
|
|
20
|
+
- type: ComponentSlot
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
style:
|
|
3
|
+
direction: row
|
|
4
|
+
align: center
|
|
5
|
+
justify: space-between
|
|
6
|
+
height: "20"
|
|
7
|
+
padding: [ "0", "4" ]
|
|
8
|
+
background: transparent
|
|
9
|
+
children:
|
|
10
|
+
- type: Box
|
|
11
|
+
style:
|
|
12
|
+
direction: row
|
|
13
|
+
align: center
|
|
14
|
+
gap: "2"
|
|
15
|
+
children:
|
|
16
|
+
- type: Icon
|
|
17
|
+
icon: sparkles
|
|
18
|
+
style:
|
|
19
|
+
width: "6"
|
|
20
|
+
height: "6"
|
|
21
|
+
color: action.primary
|
|
22
|
+
- type: Text
|
|
23
|
+
style:
|
|
24
|
+
font: title
|
|
25
|
+
color: text.primary
|
|
26
|
+
value: Assistant
|
|
27
|
+
- type: Button
|
|
28
|
+
action: close
|
|
29
|
+
style:
|
|
30
|
+
flex: 0 0 auto
|
|
31
|
+
cursor: pointer
|
|
32
|
+
width: "8"
|
|
33
|
+
height: "8"
|
|
34
|
+
radius: full
|
|
35
|
+
background: surface.base
|
|
36
|
+
border: subtle
|
|
37
|
+
shadow: sm
|
|
38
|
+
direction: row
|
|
39
|
+
align: center
|
|
40
|
+
justify: center
|
|
41
|
+
transition: all 150ms ease
|
|
42
|
+
hover:
|
|
43
|
+
background: surface.sunken
|
|
44
|
+
children:
|
|
45
|
+
- type: Icon
|
|
46
|
+
icon: close
|
|
47
|
+
style:
|
|
48
|
+
width: "4"
|
|
49
|
+
height: "4"
|
|
50
|
+
color: text.secondary
|
|
51
|
+
visibleWhen:
|
|
52
|
+
field: surfacedView
|
|
53
|
+
in: [ "" ]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
visibleWhen: hasMessages
|
|
3
|
+
style:
|
|
4
|
+
width: full
|
|
5
|
+
position: relative
|
|
6
|
+
direction: column
|
|
7
|
+
background: transparent
|
|
8
|
+
children:
|
|
9
|
+
- type: Box
|
|
10
|
+
visibleWhen:
|
|
11
|
+
field: openPanel
|
|
12
|
+
eq: faq
|
|
13
|
+
style:
|
|
14
|
+
width: full
|
|
15
|
+
direction: column
|
|
16
|
+
gap: "2"
|
|
17
|
+
background: surface.base
|
|
18
|
+
radius: lg
|
|
19
|
+
padding: "4"
|
|
20
|
+
shadow: lg
|
|
21
|
+
margin: [ "0", "0", "2", "0" ]
|
|
22
|
+
animation:
|
|
23
|
+
name: slideUp
|
|
24
|
+
duration: 0.3s
|
|
25
|
+
easing: ease-out
|
|
26
|
+
iteration: "1"
|
|
27
|
+
children:
|
|
28
|
+
- type: Box
|
|
29
|
+
style:
|
|
30
|
+
direction: row
|
|
31
|
+
justify: end
|
|
32
|
+
children:
|
|
33
|
+
- type: Button
|
|
34
|
+
action:
|
|
35
|
+
type: setTemplateValue
|
|
36
|
+
values:
|
|
37
|
+
- key: openPanel
|
|
38
|
+
value: null
|
|
39
|
+
style:
|
|
40
|
+
flex: 0 0 auto
|
|
41
|
+
cursor: pointer
|
|
42
|
+
width: "8"
|
|
43
|
+
height: "8"
|
|
44
|
+
radius: full
|
|
45
|
+
background: surface.base
|
|
46
|
+
border: subtle
|
|
47
|
+
shadow: sm
|
|
48
|
+
direction: row
|
|
49
|
+
align: center
|
|
50
|
+
justify: center
|
|
51
|
+
transition: all 150ms ease
|
|
52
|
+
hover:
|
|
53
|
+
background: surface.sunken
|
|
54
|
+
children:
|
|
55
|
+
- type: Icon
|
|
56
|
+
icon: close
|
|
57
|
+
style:
|
|
58
|
+
width: "4"
|
|
59
|
+
height: "4"
|
|
60
|
+
color: text.secondary
|
|
61
|
+
- type: Each
|
|
62
|
+
style:
|
|
63
|
+
direction: row
|
|
64
|
+
wrap: wrap
|
|
65
|
+
align: start
|
|
66
|
+
gap: "2"
|
|
67
|
+
width: full
|
|
68
|
+
template:
|
|
69
|
+
type: Button
|
|
70
|
+
action:
|
|
71
|
+
type: sendMessage
|
|
72
|
+
then:
|
|
73
|
+
type: setTemplateValue
|
|
74
|
+
values:
|
|
75
|
+
- key: openPanel
|
|
76
|
+
value: null
|
|
77
|
+
style:
|
|
78
|
+
direction: row
|
|
79
|
+
align: center
|
|
80
|
+
flex: 0 0 auto
|
|
81
|
+
maxWidth: full
|
|
82
|
+
padding: [ "2", "4" ]
|
|
83
|
+
background: surface.base
|
|
84
|
+
border: subtle
|
|
85
|
+
outline: none
|
|
86
|
+
radius: full
|
|
87
|
+
shadow: sm
|
|
88
|
+
cursor: pointer
|
|
89
|
+
transition: all 150ms ease
|
|
90
|
+
hover:
|
|
91
|
+
background: surface.sunken
|
|
92
|
+
children:
|
|
93
|
+
- type: Text
|
|
94
|
+
bind:
|
|
95
|
+
value: text
|
|
96
|
+
style:
|
|
97
|
+
font: body.sm
|
|
98
|
+
color: text.secondary
|
|
99
|
+
lineHeight: tight
|
|
100
|
+
items:
|
|
101
|
+
- text: What can you help me with?
|
|
102
|
+
- text: How does this work?
|
|
103
|
+
- text: Show me an example
|
|
104
|
+
- type: Box
|
|
105
|
+
style:
|
|
106
|
+
width: full
|
|
107
|
+
direction: row
|
|
108
|
+
justify: center
|
|
109
|
+
padding: [ "1", "0" ]
|
|
110
|
+
children:
|
|
111
|
+
- type: Button
|
|
112
|
+
action:
|
|
113
|
+
type: setTemplateValue
|
|
114
|
+
values:
|
|
115
|
+
- key: openPanel
|
|
116
|
+
value: faq
|
|
117
|
+
style:
|
|
118
|
+
direction: row
|
|
119
|
+
align: center
|
|
120
|
+
gap: "1"
|
|
121
|
+
padding: [ "1", "3" ]
|
|
122
|
+
background: surface.base
|
|
123
|
+
border: subtle
|
|
124
|
+
outline: none
|
|
125
|
+
radius: full
|
|
126
|
+
cursor: pointer
|
|
127
|
+
transition: all 150ms ease
|
|
128
|
+
hover:
|
|
129
|
+
background: surface.sunken
|
|
130
|
+
children:
|
|
131
|
+
- type: Icon
|
|
132
|
+
icon: chat
|
|
133
|
+
style:
|
|
134
|
+
width: "4"
|
|
135
|
+
height: "4"
|
|
136
|
+
color: text.secondary
|
|
137
|
+
- type: Text
|
|
138
|
+
style:
|
|
139
|
+
font: body.sm
|
|
140
|
+
color: text.secondary
|
|
141
|
+
weight: medium
|
|
142
|
+
value: Suggestions
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
style:
|
|
3
|
+
direction: row
|
|
4
|
+
gap: "1"
|
|
5
|
+
align: center
|
|
6
|
+
children:
|
|
7
|
+
- type: Box
|
|
8
|
+
style:
|
|
9
|
+
width: "1.5"
|
|
10
|
+
height: "1.5"
|
|
11
|
+
background: action.primary
|
|
12
|
+
radius: full
|
|
13
|
+
animation:
|
|
14
|
+
name: wave
|
|
15
|
+
duration: 1.6s
|
|
16
|
+
easing: ease-in-out
|
|
17
|
+
- type: Box
|
|
18
|
+
style:
|
|
19
|
+
width: "1.5"
|
|
20
|
+
height: "1.5"
|
|
21
|
+
background: action.primary
|
|
22
|
+
radius: full
|
|
23
|
+
animation:
|
|
24
|
+
name: wave
|
|
25
|
+
duration: 1.6s
|
|
26
|
+
easing: ease-in-out
|
|
27
|
+
animationDelay: 0.16s
|
|
28
|
+
- type: Box
|
|
29
|
+
style:
|
|
30
|
+
width: "1.5"
|
|
31
|
+
height: "1.5"
|
|
32
|
+
background: action.primary
|
|
33
|
+
radius: full
|
|
34
|
+
animation:
|
|
35
|
+
name: wave
|
|
36
|
+
duration: 1.6s
|
|
37
|
+
easing: ease-in-out
|
|
38
|
+
animationDelay: 0.32s
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
style:
|
|
3
|
+
direction: row
|
|
4
|
+
justify: end
|
|
5
|
+
children:
|
|
6
|
+
- type: Box
|
|
7
|
+
style:
|
|
8
|
+
direction: row
|
|
9
|
+
align: center
|
|
10
|
+
background: action.primary
|
|
11
|
+
radius: 2xl
|
|
12
|
+
radiusTopRight: sm
|
|
13
|
+
padding: [ "2", "4" ]
|
|
14
|
+
maxWidth: compact
|
|
15
|
+
children:
|
|
16
|
+
- type: Text
|
|
17
|
+
bind:
|
|
18
|
+
value: text
|
|
19
|
+
style:
|
|
20
|
+
font: body.md
|
|
21
|
+
color: action.primary-text
|
|
22
|
+
lineHeight: tight
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# FOCUSED — a component holding the conversation region, composer live beneath.
|
|
2
|
+
# A LAYOUT owned by the `focused` state: a hosted component writes `view: focused`,
|
|
3
|
+
# the ladder makes this arrangement active, this file draws it.
|
|
4
|
+
type: Box
|
|
5
|
+
style:
|
|
6
|
+
direction: row
|
|
7
|
+
width: full
|
|
8
|
+
height: full
|
|
9
|
+
overflow: hidden
|
|
10
|
+
background: surface.sunken
|
|
11
|
+
children:
|
|
12
|
+
- $include: components/core-focus
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# A GENERIC CHAT — a worked example template, shipped with Studio so templates/ is
|
|
2
|
+
# never an empty folder. It is yours: rename it, restyle it (it uses only foundation
|
|
3
|
+
# tokens, so your project's theme restyles it whole), or delete it.
|
|
4
|
+
# Guide: https://docs.unoverse.ai/design/templates.md
|
|
5
|
+
name: chat
|
|
6
|
+
description: A conversational assistant surface with a welcome hero, suggestions, and a focus overlay.
|
|
7
|
+
whenToUse: Talk to the assistant — ask a question, get help, or start any task in conversation. The conversational home and fallback surface.
|
|
8
|
+
category: Assistant
|
|
9
|
+
version: 1.0.0
|
|
10
|
+
defaultState: template
|
|
11
|
+
inputSchema:
|
|
12
|
+
type: object
|
|
13
|
+
properties:
|
|
14
|
+
message:
|
|
15
|
+
type: string
|
|
16
|
+
description: The user's request
|
|
17
|
+
|
|
18
|
+
# THE TEMPLATE TREE (State Model v2): one declaration, everything follows.
|
|
19
|
+
# Top-level order IS the priority ladder (base first, then reaction states). NESTING
|
|
20
|
+
# is containment: welcome exists only inside standard — the compiler strips it from
|
|
21
|
+
# every other arrangement — and it is the landing default. No stateOrder, no
|
|
22
|
+
# hand-written guards.
|
|
23
|
+
# NOTE: the conversation timeline is NOT declared as a substate — it is standard's own
|
|
24
|
+
# shell content (states/conversation), included by the shared chrome (components/core)
|
|
25
|
+
# and legitimately drawn under the focus overlay too. Declaring it would contain it to
|
|
26
|
+
# standard and empty the chat column in every reaction state.
|
|
27
|
+
states:
|
|
28
|
+
standard:
|
|
29
|
+
states:
|
|
30
|
+
welcome: {}
|
|
31
|
+
focused: {}
|
|
32
|
+
|
|
33
|
+
preview:
|
|
34
|
+
conversation: [ markdown-renderer ]
|
|
35
|
+
focused: [ card ]
|
|
36
|
+
|
|
37
|
+
autoTrigger: false
|
|
38
|
+
# BIND YOUR WORKFLOW to bring this to life: build one on the Canvas, then set
|
|
39
|
+
# binding: { workflow: <workflow-id>, trigger: <trigger-node-id> }
|
|
40
|
+
# The app owns its workflow; until it is bound, this template previews but cannot run.
|
|
41
|
+
layout: standard
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
visibleWhen: hasMessages
|
|
3
|
+
autoScroll: true
|
|
4
|
+
style:
|
|
5
|
+
flex: 1
|
|
6
|
+
minHeight: "0"
|
|
7
|
+
direction: column
|
|
8
|
+
overflow: auto
|
|
9
|
+
padding: [ "6", "8" ]
|
|
10
|
+
children:
|
|
11
|
+
- type: Timeline
|
|
12
|
+
style:
|
|
13
|
+
direction: column
|
|
14
|
+
gap: "4"
|
|
15
|
+
margin: [ auto, "0", "0", "0" ]
|
|
16
|
+
assistantData:
|
|
17
|
+
thinkingText: Thinking…
|
|
18
|
+
user:
|
|
19
|
+
$include: components/user-turn
|
|
20
|
+
assistant:
|
|
21
|
+
$include: components/assistant-turn
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
type: Box
|
|
2
|
+
visibleWhen: isEmpty
|
|
3
|
+
style:
|
|
4
|
+
direction: column
|
|
5
|
+
flex: 1
|
|
6
|
+
minHeight: "0"
|
|
7
|
+
overflow: hidden
|
|
8
|
+
justify: end
|
|
9
|
+
gap: "6"
|
|
10
|
+
padding: [ "8", "8", "4", "8" ]
|
|
11
|
+
children:
|
|
12
|
+
- type: Box
|
|
13
|
+
style:
|
|
14
|
+
direction: column
|
|
15
|
+
align: start
|
|
16
|
+
gap: "2"
|
|
17
|
+
maxWidth: reading
|
|
18
|
+
padding: [ "0", "8", "0", "0" ]
|
|
19
|
+
children:
|
|
20
|
+
- type: Text
|
|
21
|
+
style:
|
|
22
|
+
font: display
|
|
23
|
+
color: text.primary
|
|
24
|
+
lineHeight: none
|
|
25
|
+
align: start
|
|
26
|
+
value: How can I help?
|
|
27
|
+
- type: Text
|
|
28
|
+
style:
|
|
29
|
+
font: body.lg
|
|
30
|
+
color: text.secondary
|
|
31
|
+
align: start
|
|
32
|
+
value: Ask a question, or pick a place to start.
|
|
33
|
+
- type: Each
|
|
34
|
+
style:
|
|
35
|
+
direction: row
|
|
36
|
+
align: start
|
|
37
|
+
gap: "4"
|
|
38
|
+
width: full
|
|
39
|
+
padding: [ "2", "0", "2", "0" ]
|
|
40
|
+
template:
|
|
41
|
+
type: Button
|
|
42
|
+
action: sendMessage
|
|
43
|
+
style:
|
|
44
|
+
position: relative
|
|
45
|
+
flex: 1 1 0
|
|
46
|
+
minWidth: 0
|
|
47
|
+
height: "90"
|
|
48
|
+
padding: "0"
|
|
49
|
+
radius: 2xl
|
|
50
|
+
border: none
|
|
51
|
+
overflow: hidden
|
|
52
|
+
cursor: pointer
|
|
53
|
+
transition: all 200ms ease
|
|
54
|
+
shadow: lg
|
|
55
|
+
hover:
|
|
56
|
+
shadow: xl
|
|
57
|
+
transform: scale(1.02)
|
|
58
|
+
children:
|
|
59
|
+
- type: Image
|
|
60
|
+
bind:
|
|
61
|
+
src: image
|
|
62
|
+
alt: title
|
|
63
|
+
style:
|
|
64
|
+
position: absolute
|
|
65
|
+
inset: "0"
|
|
66
|
+
width: full
|
|
67
|
+
height: full
|
|
68
|
+
fit: cover
|
|
69
|
+
- type: Box
|
|
70
|
+
style:
|
|
71
|
+
position: absolute
|
|
72
|
+
inset: "0"
|
|
73
|
+
direction: column
|
|
74
|
+
align: start
|
|
75
|
+
justify: end
|
|
76
|
+
gap: "2"
|
|
77
|
+
padding: "4"
|
|
78
|
+
background: overlay.scrim
|
|
79
|
+
textAlign: start
|
|
80
|
+
children:
|
|
81
|
+
- type: Text
|
|
82
|
+
bind:
|
|
83
|
+
value: tag
|
|
84
|
+
style:
|
|
85
|
+
font: caption
|
|
86
|
+
color: white
|
|
87
|
+
lineHeight: tight
|
|
88
|
+
- type: Text
|
|
89
|
+
bind:
|
|
90
|
+
value: title
|
|
91
|
+
style:
|
|
92
|
+
font: title
|
|
93
|
+
color: white
|
|
94
|
+
lineHeight: tight
|
|
95
|
+
align: start
|
|
96
|
+
- type: Text
|
|
97
|
+
bind:
|
|
98
|
+
value: subtitle
|
|
99
|
+
style:
|
|
100
|
+
font: body.sm
|
|
101
|
+
color: white
|
|
102
|
+
lineHeight: snug
|
|
103
|
+
align: start
|
|
104
|
+
items:
|
|
105
|
+
- image: https://picsum.photos/seed/explore/840/1160
|
|
106
|
+
tag: Explore
|
|
107
|
+
title: What can you do?
|
|
108
|
+
subtitle: See what this assistant can help with.
|
|
109
|
+
text: What can you help me with?
|
|
110
|
+
- image: https://picsum.photos/seed/learn/840/1160
|
|
111
|
+
tag: Learn
|
|
112
|
+
title: Show me around
|
|
113
|
+
subtitle: A quick tour of how this works.
|
|
114
|
+
text: Give me a quick tour of what I can do here
|
|
115
|
+
- image: https://picsum.photos/seed/start/840/1160
|
|
116
|
+
tag: Start
|
|
117
|
+
title: Jump right in
|
|
118
|
+
subtitle: Describe what you need in your own words.
|
|
119
|
+
text: I would like some help getting started
|
package/local/scaffold.mjs
CHANGED
|
@@ -43,7 +43,11 @@ export function checkProjectName(name) {
|
|
|
43
43
|
const WELCOME = [
|
|
44
44
|
"unoverse: '1.0'",
|
|
45
45
|
"kind: component",
|
|
46
|
-
|
|
46
|
+
// name === filename (the item-key rule the publish lint enforces), and the closed
|
|
47
|
+
// style vocabulary has `font`, not `fontSize` — both caught by linting the scaffold
|
|
48
|
+
// output itself, which is the guard: the first component a developer ever sees must
|
|
49
|
+
// lint clean, or every rule the platform states is undermined by its own starter.
|
|
50
|
+
"name: welcome",
|
|
47
51
|
"category: Content",
|
|
48
52
|
"description: A first component. Edit this file and Studio updates as you save.",
|
|
49
53
|
"root:",
|
|
@@ -52,7 +56,7 @@ const WELCOME = [
|
|
|
52
56
|
" children:",
|
|
53
57
|
" - type: Text",
|
|
54
58
|
" value: Hello from your first component",
|
|
55
|
-
" style: {
|
|
59
|
+
" style: { font: 'body.lg', color: 'text.primary' }",
|
|
56
60
|
"",
|
|
57
61
|
].join("\n");
|
|
58
62
|
|
|
@@ -81,6 +85,15 @@ export function createProject(home, name) {
|
|
|
81
85
|
if (existsSync(sampleStyles) && readdirSync(join(root, "styles")).length === 0) {
|
|
82
86
|
cpSync(sampleStyles, join(root, "styles"), { recursive: true });
|
|
83
87
|
}
|
|
88
|
+
|
|
89
|
+
// A worked example TEMPLATE (a generic chat: welcome hero, suggestions, focus
|
|
90
|
+
// overlay — the bpp-chat-layout shape, neutralized to foundation tokens), so
|
|
91
|
+
// templates/ is never an empty folder and the first app is a rename away. Same
|
|
92
|
+
// rule as the styles: copied only into an empty folder, then the developer's.
|
|
93
|
+
const sampleTemplate = join(dirname(fileURLToPath(import.meta.url)), "sample-template/chat");
|
|
94
|
+
if (existsSync(sampleTemplate) && readdirSync(join(root, "templates")).length === 0) {
|
|
95
|
+
cpSync(sampleTemplate, join(root, "templates/chat"), { recursive: true });
|
|
96
|
+
}
|
|
84
97
|
return root;
|
|
85
98
|
}
|
|
86
99
|
|
package/package.json
CHANGED
|
@@ -519,6 +519,11 @@ export function TemplatesView({
|
|
|
519
519
|
nodeId: `${c.name}-${i}`,
|
|
520
520
|
component: {
|
|
521
521
|
type: c.name,
|
|
522
|
+
// The ADDRESS, exactly as a real server arrival carries it: the type alone
|
|
523
|
+
// cannot rebuild it (names are unique only within a tier — store.ts), so a
|
|
524
|
+
// mock seed without it resolved bare `unoverse://components/<name>` and the
|
|
525
|
+
// resolver rightly refused to guess between two orgs' clones.
|
|
526
|
+
componentUrl: `unoverse://components/${c.org ? `${c.org}/` : ""}${c.name}`,
|
|
522
527
|
props: { ...defaultsOf(c), ...restState, defaultState: "inline" },
|
|
523
528
|
},
|
|
524
529
|
});
|
|
@@ -545,9 +550,12 @@ export function TemplatesView({
|
|
|
545
550
|
Promise.all([
|
|
546
551
|
// The header's org scope filters the list ("all" → every org's apps, each
|
|
547
552
|
// tagged); the selected app's org still picks its own preview theme.
|
|
548
|
-
// Components are universal
|
|
553
|
+
// Components are SCOPED like the templates: an org sees "universal + its own".
|
|
554
|
+
// Fetched unscoped, a cloned org's same-named component (design/lyceum cloned
|
|
555
|
+
// from design/bpp → two bare `journey-finder`s) sat beside the original and
|
|
556
|
+
// Mock seeding picked whichever listed first.
|
|
549
557
|
authedFetch(`/dev/templates${org !== "all" ? `?org=${encodeURIComponent(org)}` : ""}`).then((r) => (r.ok ? r.json() : { templates: [] })),
|
|
550
|
-
authedFetch(
|
|
558
|
+
authedFetch(`/dev/components${org !== "all" ? `?org=${encodeURIComponent(org)}` : ""}`).then((r) => (r.ok ? r.json() : { components: [] })),
|
|
551
559
|
])
|
|
552
560
|
.then(([t, c]) => {
|
|
553
561
|
const tl: Def[] = t.templates ?? [];
|
|
@@ -599,7 +607,14 @@ export function TemplatesView({
|
|
|
599
607
|
const wantComponent = Boolean(layoutSel) || Boolean((activeState?.where as { field?: string } | undefined)?.field);
|
|
600
608
|
let seededSet: Def[] = [];
|
|
601
609
|
if (!wantEmpty) {
|
|
602
|
-
|
|
610
|
+
// ORG-AWARE: names are unique only within a tier, so a bare preview ref must
|
|
611
|
+
// resolve to the SELECTED APP's own org first, then the universal tier — never
|
|
612
|
+
// to another org's same-named clone (which the "all" scope still lists).
|
|
613
|
+
const appOrg = (selectedDef?.org ?? "").toLowerCase();
|
|
614
|
+
const byName = (name: string) => {
|
|
615
|
+
const hits = components.filter((c) => c.name.toLowerCase() === name.toLowerCase());
|
|
616
|
+
return hits.find((c) => (c.org ?? "").toLowerCase() === appOrg) ?? hits.find((c) => !c.org) ?? hits[0];
|
|
617
|
+
};
|
|
603
618
|
const set = entry
|
|
604
619
|
? entry.map(byName).filter((c): c is Def => Boolean(c))
|
|
605
620
|
: wantComponent
|