Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
C
coderai
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
coderai
Commits
1971ce1d
Commit
1971ce1d
authored
May 06, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: show studio capability notes in outputs
parent
0c71c53a
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
51 additions
and
0 deletions
+51
-0
chat.html
codai/admin/templates/chat.html
+42
-0
test_studio_composed_surfaces.py
tests/test_studio_composed_surfaces.py
+9
-0
No files found.
codai/admin/templates/chat.html
View file @
1971ce1d
...
@@ -167,6 +167,14 @@ a.dl { display:inline-block; margin-top:.4rem; }
...
@@ -167,6 +167,14 @@ a.dl { display:inline-block; margin-top:.4rem; }
.cap-chip.dim
{
opacity
:
.72
;
}
.cap-chip.dim
{
opacity
:
.72
;
}
.cap-missing
,
.cap-note
{
font-size
:
12px
;
color
:
var
(
--text-2
);
}
.cap-missing
,
.cap-note
{
font-size
:
12px
;
color
:
var
(
--text-2
);
}
.cap-note
strong
,
.cap-missing
strong
{
color
:
var
(
--text-1
);
}
.cap-note
strong
,
.cap-missing
strong
{
color
:
var
(
--text-1
);
}
.cap-output-note
{
width
:
100%
;
max-width
:
960px
;
border
:
1px
solid
rgba
(
245
,
158
,
11
,
.24
);
background
:
rgba
(
58
,
37
,
16
,
.72
);
color
:
#f6d08a
;
border-radius
:
8px
;
padding
:
.75rem
.9rem
;
display
:
flex
;
flex-direction
:
column
;
gap
:
.35rem
;
}
.cap-output-note.unavailable
{
border-color
:
rgba
(
248
,
113
,
113
,
.24
);
background
:
rgba
(
80
,
28
,
28
,
.62
);
color
:
#f3b2b2
;
}
.cap-output-note
ul
{
margin
:
0
;
padding-left
:
1.1rem
;
}
.cap-toggle
{
font-size
:
12px
;
display
:
flex
;
align-items
:
center
;
gap
:
.4rem
;
cursor
:
pointer
;
color
:
var
(
--text-2
);
}
.cap-toggle
{
font-size
:
12px
;
display
:
flex
;
align-items
:
center
;
gap
:
.4rem
;
cursor
:
pointer
;
color
:
var
(
--text-2
);
}
.cap-toggle
input
{
margin
:
0
;
}
.cap-toggle
input
{
margin
:
0
;
}
.cap-preserve-note
{
.cap-preserve-note
{
...
@@ -1725,6 +1733,37 @@ function renderCapabilityCards() {
...
@@ -1725,6 +1733,37 @@ function renderCapabilityCards() {
renderAudioBackendHealth
();
renderAudioBackendHealth
();
}
}
function
renderCapabilityOutputNote
(
sub
,
outEl
)
{
if
(
!
outEl
)
return
;
const
details
=
getCapabilityDetails
(
sub
);
const
noteId
=
`cap-output-note-
${
sub
}
`
;
outEl
.
querySelector
(
`#
${
noteId
}
`
)?.
remove
();
if
(
!
details
||
details
.
availability
===
'available'
)
return
;
const
missing
=
[];
if
(
details
.
missingRequired
.
length
)
missing
.
push
(...
details
.
missingRequired
.
map
(
item
=>
`Missing required:
${
item
}
`
));
if
(
details
.
missingOptional
.
length
)
missing
.
push
(...
details
.
missingOptional
.
map
(
item
=>
`Limited without:
${
item
}
`
));
if
(
!
missing
.
length
&&
Array
.
isArray
(
details
.
notes
))
missing
.
push
(...
details
.
notes
);
const
title
=
details
.
availability
===
'unavailable'
?
'Feature unavailable'
:
'Feature partially available'
;
const
detailHtml
=
missing
.
length
?
`<ul>
${
missing
.
map
(
item
=>
`<li>
${
escapeHtml
(
item
)}
</li>`
).
join
(
''
)}
</ul>`
:
'<div>Some required pieces are missing for this surface.</div>'
;
outEl
.
insertAdjacentHTML
(
'afterbegin'
,
`
<div class="cap-output-note
${
details
.
availability
===
'unavailable'
?
'unavailable'
:
''
}
" id="
${
noteId
}
">
<strong>
${
title
}
</strong>
${
detailHtml
}
</div>
`
);
}
function
renderOutputCapabilityNotes
()
{
Object
.
keys
(
SUB_PANEL_ALIAS
).
forEach
(
sub
=>
{
const
panelId
=
SUB_PANEL_ALIAS
[
sub
]
||
(
`panel-
${
sub
}
`
);
const
panel
=
$
(
panelId
);
const
outEl
=
panel
?.
querySelector
(
'.gen-out'
);
renderCapabilityOutputNote
(
sub
,
outEl
);
});
}
function
summarizeDiagnostics
()
{
function
summarizeDiagnostics
()
{
const
subStates
=
currentTabState
.
subs
||
{};
const
subStates
=
currentTabState
.
subs
||
{};
const
details
=
Object
.
entries
(
SUB_CAPABILITY_RULES
).
map
(([
sub
,
rule
])
=>
{
const
details
=
Object
.
entries
(
SUB_CAPABILITY_RULES
).
map
(([
sub
,
rule
])
=>
{
...
@@ -2348,6 +2387,7 @@ function updateTabs(m) {
...
@@ -2348,6 +2387,7 @@ function updateTabs(m) {
$
(
'attach-btn'
).
style
.
display
=
caps
.
has
(
'image_to_text'
)
?
''
:
'none'
;
$
(
'attach-btn'
).
style
.
display
=
caps
.
has
(
'image_to_text'
)
?
''
:
'none'
;
renderCapabilityCards
();
renderCapabilityCards
();
renderDiagnostics
();
renderDiagnostics
();
renderOutputCapabilityNotes
();
const
activeCatBtn
=
document
.
querySelector
(
'.t1btn.active'
);
const
activeCatBtn
=
document
.
querySelector
(
'.t1btn.active'
);
const
nextCat
=
activeCatBtn
?.
dataset
.
cat
||
'chat'
;
const
nextCat
=
activeCatBtn
?.
dataset
.
cat
||
'chat'
;
selectCat
(
nextCat
);
selectCat
(
nextCat
);
...
@@ -2387,6 +2427,8 @@ function selectSub(sub) {
...
@@ -2387,6 +2427,8 @@ function selectSub(sub) {
const
panelId
=
SUB_PANEL_ALIAS
[
sub
]
||
(
'panel-'
+
sub
);
const
panelId
=
SUB_PANEL_ALIAS
[
sub
]
||
(
'panel-'
+
sub
);
const
panel
=
$
(
panelId
);
const
panel
=
$
(
panelId
);
if
(
panel
)
panel
.
classList
.
add
(
'active'
);
if
(
panel
)
panel
.
classList
.
add
(
'active'
);
const
outEl
=
panel
?.
querySelector
(
'.gen-out'
);
if
(
outEl
)
renderCapabilityOutputNote
(
sub
,
outEl
);
// When switching to vid-faceswap, pre-select video mode
// When switching to vid-faceswap, pre-select video mode
if
(
sub
===
'vid-faceswap'
)
{
const
t
=
$
(
'fs-type'
);
if
(
t
)
{
t
.
value
=
'video'
;
fsFaceSwapTypeChange
();
}
}
if
(
sub
===
'vid-faceswap'
)
{
const
t
=
$
(
'fs-type'
);
if
(
t
)
{
t
.
value
=
'video'
;
fsFaceSwapTypeChange
();
}
}
if
(
sub
===
'vid-outfit'
)
{
const
t
=
$
(
'ot-type'
);
if
(
t
)
{
t
.
value
=
'video'
;
otOutfitTypeChange
();
}
}
if
(
sub
===
'vid-outfit'
)
{
const
t
=
$
(
'ot-type'
);
if
(
t
)
{
t
.
value
=
'video'
;
otOutfitTypeChange
();
}
}
...
...
tests/test_studio_composed_surfaces.py
View file @
1971ce1d
...
@@ -357,3 +357,12 @@ def test_studio_generation_panel_uses_wider_control_column():
...
@@ -357,3 +357,12 @@ def test_studio_generation_panel_uses_wider_control_column():
assert
".gen-ctrl { width:min(380px,36vw); min-width:340px; max-width:420px;"
in
text
assert
".gen-ctrl { width:min(380px,36vw); min-width:340px; max-width:420px;"
in
text
assert
"@media (max-width: 720px) {"
in
text
assert
"@media (max-width: 720px) {"
in
text
def
test_studio_output_surfaces_capability_warnings
():
template_path
=
"/storage/coderai/.worktrees/web-admin-polish/codai/admin/templates/chat.html"
text
=
open
(
template_path
,
"r"
,
encoding
=
"utf-8"
)
.
read
()
assert
"cap-output-note"
in
text
assert
"renderCapabilityOutputNote"
in
text
assert
"renderOutputCapabilityNotes"
in
text
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment