Skip to content

Commit baf0075

Browse files
committed
Role Assignment機能: Role×プロバイダーのマトリクスUIでRole別プロバイダー割当
- プロバイダーボタン横にRoleボタン追加、クリックで専用モーダル表示 - T2I/I2I/Inpaint/Upscale/RemoveBG/Angleの6 Roleを個別にプロバイダー割当可能 - Default選択時はアクティブプロバイダーに従う既存動作を維持 - サポート外のRole×Provider組み合わせはグレーアウト表示 - provider-registryにroleAssignments管理を追加 - ai-management.jsのディスパッチをgetProviderForRole()経由に変更 - 設定はlocalStorageに自動保存/復元 - 全8言語の翻訳対応 https://claude.ai/code/session_016zeauHzccdNZMjE9cGzkPN
1 parent b95c019 commit baf0075

File tree

8 files changed

+287
-32
lines changed

8 files changed

+287
-32
lines changed

css/ui/role-assign-modal.css

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,7 @@
16391639
Fal.ai
16401640
</button>
16411641

1642+
<button class="role-assign-trigger" onclick="roleAssignmentUI.open()" data-i18n="roleAssignButton">&#9881; Role</button>
16421643
<div id="apiSettingsUrlHelpe">
16431644
</div>
16441645
</div>
@@ -1855,7 +1856,40 @@ <h2 data-i18n="shortcutTitle">Keyboard Shortcuts</h2>
18551856
</div>
18561857
</div>
18571858

1859+
<!-- ========== Modal: Role Assignment ========== -->
1860+
<div id="roleAssignModal" class="role-assign-modal" style="display:none;">
1861+
<div class="role-assign-modal-overlay" onclick="roleAssignmentUI.close()"></div>
1862+
<div class="role-assign-modal-content">
1863+
<div class="role-assign-modal-header">
1864+
<h2 data-i18n="roleAssignTitle">&#9881; Role Assignment</h2>
1865+
<button class="role-assign-modal-close" onclick="roleAssignmentUI.close()">&times;</button>
1866+
</div>
1867+
<div class="role-assign-modal-body">
1868+
<table class="role-matrix">
1869+
<thead>
1870+
<tr>
1871+
<th data-i18n="roleAssignRole">Role</th>
1872+
<th data-i18n="roleDefault">Default</th>
1873+
<th>ComfyUI</th>
1874+
<th>WebUI</th>
1875+
<th>RunPod</th>
1876+
<th>Serverless</th>
1877+
<th>Fal.ai</th>
1878+
</tr>
1879+
</thead>
1880+
<tbody id="roleMatrixBody"></tbody>
1881+
</table>
1882+
<div class="role-assign-note" data-i18n="roleAssignNote">&#8251; Greyed out = provider does not support this role. Default = follows the active provider.</div>
1883+
</div>
1884+
<div class="role-assign-modal-footer">
1885+
<button class="role-assign-btn-cancel" onclick="roleAssignmentUI.close()" data-i18n="roleAssignCancel">Cancel</button>
1886+
<button class="role-assign-btn-submit" onclick="roleAssignmentUI.apply()" data-i18n="roleAssignApply">Apply</button>
1887+
</div>
1888+
</div>
1889+
</div>
1890+
18581891
<!-- ========== Additional CSS ========== -->
1892+
<link rel="stylesheet" href="css/ui/role-assign-modal.css?v=7.3" media="print" onload="this.media='all'">
18591893
<link rel="stylesheet" href="css/ui/shortcut-modal.css?v=7.2" media="print" onload="this.media='all'">
18601894
<link rel="stylesheet" href="third/intro/introjs.min.css">
18611895
<link rel="stylesheet" href="css/ui/intro.css?" media="print" onload="this.media='all'">
@@ -1969,6 +2003,7 @@ <h2 data-i18n="shortcutTitle">Keyboard Shortcuts</h2>
19692003
<script src="js/ai/comfyui/comfyui-management.js?v=7.2" defer></script>
19702004
<script src="js/ai/ai-settings.js?v=7.2" defer></script>
19712005
<script src="js/ai/ai-management.js?v=7.2" defer></script>
2006+
<script src="js/ai/role/role-assignment-ui.js?v=7.3" defer></script>
19722007
<script src="js/ai/inpainting/inpaint-mask.js?v=7.3" defer></script>
19732008
<script src="js/ai/inpainting/inpaint-workflow.js?v=7.3" defer></script>
19742009
<script src="js/ai/inpainting/inpaint-editor.js?v=7.3" defer></script>

js/ai/ai-management.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,45 +54,45 @@ return sdCleared+comfyCleared+rpCleared+falCleared;
5454

5555

5656
async function T2I(layer,spinner){
57-
var provider=providerRegistry.getActive();
58-
if(provider&&provider.supportsRole(AI_ROLES.Text2Image)){
57+
var provider=providerRegistry.getProviderForRole(AI_ROLES.Text2Image);
58+
if(provider){
5959
return provider.executeT2I(layer,spinner.id);
6060
}
6161
}
6262
function I2I(layer,spinner){
63-
var provider=providerRegistry.getActive();
64-
if(provider&&provider.supportsRole(AI_ROLES.Image2Image)){
63+
var provider=providerRegistry.getProviderForRole(AI_ROLES.Image2Image);
64+
if(provider){
6565
return provider.executeI2I(layer,spinner.id);
6666
}
6767
}
6868

6969
async function aiRembg(layer,spinner){
70-
var provider=providerRegistry.getActive();
71-
if(provider&&provider.supportsRole(AI_ROLES.RemoveBG)){
70+
var provider=providerRegistry.getProviderForRole(AI_ROLES.RemoveBG);
71+
if(provider){
7272
return provider.executeRembg(layer,spinner.id);
7373
}
7474
}
7575

7676
async function aiUpscale(layer,spinner){
77-
var provider=providerRegistry.getActive();
78-
if(provider&&provider.supportsRole(AI_ROLES.Upscaler)){
77+
var provider=providerRegistry.getProviderForRole(AI_ROLES.Upscaler);
78+
if(provider){
7979
return provider.executeUpscale(layer,spinner.id);
8080
}
8181
}
8282

8383
function canUseInpaint(){
84-
var provider=providerRegistry.getActive();
85-
return provider!==null&&provider.canUseInpaint()&&hasRole(AI_ROLES.Inpaint);
84+
var provider=providerRegistry.getProviderForRole(AI_ROLES.Inpaint);
85+
return provider!==null&&provider.canUseInpaint();
8686
}
8787

8888
function canUseAngle(){
89-
var provider=providerRegistry.getActive();
90-
return provider!==null&&provider.canUseAngle()&&hasRole(AI_ROLES.I2I_Angle);
89+
var provider=providerRegistry.getProviderForRole(AI_ROLES.I2I_Angle);
90+
return provider!==null&&provider.canUseAngle();
9191
}
9292

9393
function AngleGenerate(layer,spinner,anglePrompt){
94-
var provider=providerRegistry.getActive();
95-
if(provider&&provider.supportsRole(AI_ROLES.I2I_Angle)){
94+
var provider=providerRegistry.getProviderForRole(AI_ROLES.I2I_Angle);
95+
if(provider){
9696
return provider.executeAngle(layer,spinner.id,anglePrompt);
9797
}
9898
}

js/ai/provider/provider-registry.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ var providerRegistry=(function(){
33
var providers={};
44
var activeProviderId=null;
55
var apiModeToProviderId={};
6+
var roleAssignments={};
67
var registryLogger=new SimpleLogger('providerRegistry',LogLevel.DEBUG);
78
function register(provider){
89
providers[provider.id]=provider;
@@ -42,7 +43,40 @@ return providers[activeProviderId]||null;
4243
function getActiveId(){
4344
return activeProviderId;
4445
}
46+
function setRoleAssignment(role,providerId){
47+
if(providerId==='default'||!providerId){
48+
delete roleAssignments[role];
49+
}else{
50+
roleAssignments[role]=providerId;
51+
}
52+
registryLogger.debug('Role assignment: '+role+' -> '+(providerId||'default'));
53+
}
54+
function getRoleAssignment(role){
55+
return roleAssignments[role]||'default';
56+
}
57+
function getAllRoleAssignments(){
58+
return Object.assign({},roleAssignments);
59+
}
60+
function loadRoleAssignments(data){
61+
roleAssignments={};
62+
if(data&&typeof data==='object'){
63+
Object.keys(data).forEach(function(key){
64+
if(data[key]&&data[key]!=='default'){
65+
roleAssignments[key]=data[key];
66+
}
67+
});
68+
}
69+
}
4570
function getProviderForRole(role){
71+
var assignedId=roleAssignments[role];
72+
if(assignedId&&assignedId!=='default'){
73+
var provider=providers[assignedId];
74+
if(provider&&provider.supportsRole(role)){
75+
return provider;
76+
}
77+
registryLogger.debug('Assigned provider '+assignedId+' does not support role: '+role);
78+
return null;
79+
}
4680
var active=getActive();
4781
if(active&&active.supportsRole(role)){
4882
return active;
@@ -58,6 +92,10 @@ syncFromApiMode:syncFromApiMode,
5892
setActive:setActive,
5993
getActive:getActive,
6094
getActiveId:getActiveId,
95+
setRoleAssignment:setRoleAssignment,
96+
getRoleAssignment:getRoleAssignment,
97+
getAllRoleAssignments:getAllRoleAssignments,
98+
loadRoleAssignments:loadRoleAssignments,
6199
getProviderForRole:getProviderForRole
62100
};
63101
})();

js/ai/role/ai-roles.js

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
// if( hasNotRole( AI_ROLES.XXXXX )){return;}
2-
// if( hasRole( AI_ROLES.XXXXX )){return;}
3-
4-
//WebUI : SDXL
5-
//Forge : SDXL, Flux
6-
//ComfyUI : SDXL, Flux
7-
1+
// AIロール定義とロール判定
82
const AI_ROLES={
93
Text2Image: "Text2Image",
104
Image2Image: "Image2Image",
@@ -58,21 +52,22 @@ AI_ROLES.RemoveBG
5852
]
5953
};
6054

55+
const ROLE_ASSIGNABLE_ROLES=[
56+
AI_ROLES.Text2Image,
57+
AI_ROLES.Image2Image,
58+
AI_ROLES.Inpaint,
59+
AI_ROLES.Upscaler,
60+
AI_ROLES.RemoveBG,
61+
AI_ROLES.I2I_Angle
62+
];
63+
6164
function hasNotRole(role) {
6265
return!(hasRole(role));
6366
}
6467

6568
function hasRole(role) {
66-
if (apiMode==apis.A1111) {
67-
return roles.A1111.includes(role);
68-
} else if (apiMode==apis.COMFYUI) {
69-
return roles.COMFYUI.includes(role);
70-
} else if (apiMode==apis.RUNPOD_COMFYUI) {
71-
return roles.RUNPOD_COMFYUI.includes(role);
72-
} else if (apiMode==apis.RUNPOD_ENDPOINT) {
73-
return roles.RUNPOD_ENDPOINT.includes(role);
74-
} else if (apiMode==apis.FAL_AI) {
75-
return roles.FAL_AI.includes(role);
69+
if(providerRegistry.getProviderForRole(role)!==null){
70+
return true;
7671
}
7772
return false;
78-
}
73+
}

js/ai/role/role-assignment-ui.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Role Assignmentモーダル: Role×プロバイダーのマトリクスUI
2+
var roleAssignmentUI=(function(){
3+
var raLogger=new SimpleLogger('roleAssignUI',LogLevel.DEBUG);
4+
var PROVIDER_COLUMNS=[
5+
{id:'default',labelKey:'roleDefault'},
6+
{id:'localComfyUI',label:'ComfyUI'},
7+
{id:'localSDWebUI',label:'WebUI'},
8+
{id:'runpodComfyUI',label:'RunPod'},
9+
{id:'runpodEndpoint',label:'Serverless'},
10+
{id:'falai',label:'Fal.ai'}
11+
];
12+
var ROLE_ROWS=[
13+
{role:AI_ROLES.Text2Image,label:'T2I'},
14+
{role:AI_ROLES.Image2Image,label:'I2I'},
15+
{role:AI_ROLES.Inpaint,label:'Inpaint'},
16+
{role:AI_ROLES.Upscaler,label:'Upscale'},
17+
{role:AI_ROLES.RemoveBG,label:'RemoveBG'},
18+
{role:AI_ROLES.I2I_Angle,label:'Angle'}
19+
];
20+
var tempAssignments={};
21+
function open(){
22+
tempAssignments={};
23+
ROLE_ROWS.forEach(function(row){
24+
tempAssignments[row.role]=providerRegistry.getRoleAssignment(row.role);
25+
});
26+
buildMatrix();
27+
$('roleAssignModal').style.display='';
28+
}
29+
function close(){
30+
$('roleAssignModal').style.display='none';
31+
}
32+
function apply(){
33+
ROLE_ROWS.forEach(function(row){
34+
providerRegistry.setRoleAssignment(row.role,tempAssignments[row.role]);
35+
});
36+
close();
37+
updateLayerPanel();
38+
raLogger.info('Role assignments applied');
39+
debouncedSettingsSave();
40+
}
41+
function buildMatrix(){
42+
var tbody=$('roleMatrixBody');
43+
tbody.innerHTML='';
44+
ROLE_ROWS.forEach(function(row){
45+
var tr=document.createElement('tr');
46+
var tdLabel=document.createElement('td');
47+
tdLabel.textContent=row.label;
48+
tr.appendChild(tdLabel);
49+
PROVIDER_COLUMNS.forEach(function(col){
50+
var td=document.createElement('td');
51+
var radio=document.createElement('input');
52+
radio.type='radio';
53+
radio.name='roleAssign_'+row.role;
54+
radio.className='role-radio';
55+
radio.value=col.id;
56+
if(col.id==='default'){
57+
radio.checked=(tempAssignments[row.role]==='default');
58+
}else{
59+
var provider=providerRegistry.get(col.id);
60+
if(!provider||!provider.supportsRole(row.role)){
61+
radio.disabled=true;
62+
td.className='role-provider-unavailable';
63+
}
64+
radio.checked=(tempAssignments[row.role]===col.id);
65+
}
66+
radio.addEventListener('change',function(){
67+
if(this.checked){
68+
tempAssignments[row.role]=col.id;
69+
}
70+
});
71+
td.appendChild(radio);
72+
tr.appendChild(td);
73+
});
74+
tbody.appendChild(tr);
75+
});
76+
}
77+
return{
78+
open:open,
79+
close:close,
80+
apply:apply
81+
};
82+
})();

js/project-management.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,9 @@ else if(mode===apis.RUNPOD_COMFYUI)changeExternalAPI($('runpodComfyUIButton'));
325325
else if(mode===apis.RUNPOD_ENDPOINT)changeExternalAPI($('runpodEndpointButton'));
326326
else if(mode===apis.FAL_AI)changeExternalAPI($('falaiButton'));
327327
else changeExternalAPI($('comfyUIButton'));
328+
if(data.roleAssignments){
329+
providerRegistry.loadRoleAssignments(data.roleAssignments);
330+
}
328331
}
329332

330333
function saveSettingsLocalStrage(silent){
@@ -340,6 +343,7 @@ Object.keys(BASEPROMPT_SCHEMA).forEach(function(key){
340343
var cfg=BASEPROMPT_SCHEMA[key];
341344
data[key]=basePrompt[cfg.key];
342345
});
346+
data.roleAssignments=providerRegistry.getAllRoleAssignments();
343347
localStorage.setItem('localSettingsData',JSON.stringify(data));
344348
}
345349

0 commit comments

Comments
 (0)