Files
ITWorx_MCP_Hub-Public/services/runtime/gitea_operations_widget.py
T

39 lines
7.5 KiB
Python

# ruff: noqa: E501
"""Self-contained standards-first MCP Apps operations surface."""
from __future__ import annotations
OPERATIONS_WIDGET_URI = "ui://itworx-gitea/operations-v4.html"
OPERATIONS_WIDGET_HTML = r"""<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Gitea autonomous workspace</title>
<style>
:root{color-scheme:light dark;font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;--fg:#202124}:root[data-theme="dark"]{--fg:#eceff4;color-scheme:dark}:root[data-theme="light"]{--fg:#202124;color-scheme:light}*{box-sizing:border-box}
body{margin:0;padding:14px;background:transparent;color:var(--fg)}.shell{display:grid;gap:12px}.top{display:flex;gap:10px;align-items:flex-start;justify-content:space-between}h1{font-size:17px;margin:0 0 4px}.muted{opacity:.68;font-size:12px}
.pill{border-radius:999px;padding:5px 9px;font-size:12px;font-weight:650;background:#e9edf5;color:#233044}.ok{background:#daf3df;color:#145c26}.warn{background:#fff0c2;color:#6a4c00}
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:9px}.card{border:1px solid color-mix(in srgb,currentColor 18%,transparent);border-radius:10px;padding:10px;min-width:0}.label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;opacity:.62}.value{font-size:13px;font-weight:600;margin-top:4px;overflow-wrap:anywhere}
ul{list-style:none;margin:6px 0 0;padding:0;display:grid;gap:5px}li{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;font-size:12px;border-bottom:1px solid color-mix(in srgb,currentColor 10%,transparent);padding:5px 0}li:last-child{border:0}
button{font:inherit;border:1px solid color-mix(in srgb,currentColor 25%,transparent);background:transparent;color:inherit;border-radius:8px;padding:7px 10px;cursor:pointer}button:hover{background:color-mix(in srgb,currentColor 7%,transparent)}button:disabled{opacity:.45;cursor:default}.actions{display:flex;gap:7px;flex-wrap:wrap}.empty{font-size:12px;opacity:.66}.error{color:#a12626;font-size:12px}
@media(prefers-color-scheme:dark){:root:not([data-theme="light"]){--fg:#eceff4}.pill{background:#303849;color:#edf2ff}.ok{background:#183f23;color:#b9f1c5}.warn{background:#4a390d;color:#ffe8a3}}
</style>
</head>
<body><main class="shell" id="root" aria-live="polite"><div class="muted">Loading workspace operations…</div></main>
<script>
(() => {
const root=document.getElementById('root');let latest=null;let nextId=1;const pending=new Map();const nl=(document.documentElement.lang||navigator.language||'en').toLowerCase().startsWith('nl');const t=(en,nlText)=>nl?nlText:en;document.title=t('Gitea autonomous workspace','Autonome Gitea-werkruimte');
const el=(tag,value='',cls='')=>{const node=document.createElement(tag);node.textContent=value??'';if(cls)node.className=cls;return node};const card=(label,value)=>{const node=el('div','','card');node.append(el('div',label,'label'),el('div',value||'—','value'));return node};const unwrap=value=>value?.structuredContent?.data??value?.structuredContent??value?.data??value?.toolOutput?.structuredContent?.data??value?.toolOutput?.data??value;
function request(method,params){const id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id,method,params},'*');return new Promise((resolve,reject)=>{const timeout=setTimeout(()=>{pending.delete(id);reject(new Error('MCP Apps bridge timeout'))},4000);pending.set(id,{resolve,reject,timeout})})}function setTheme(theme){if(theme==='dark'||theme==='light')document.documentElement.dataset.theme=theme}
function render(raw){const data=unwrap(raw);if(!data||typeof data!=='object'){root.replaceChildren(el('div',t('No operations data returned.','Geen operatiegegevens ontvangen.'),'empty'));return}latest=data;const items=Array.isArray(data.items)?data.items:[];const summary=data.summary||{};const connected=data.connected??true;const heading=el('div');heading.append(el('h1',data.workspaceId?t('Autonomous repository workspace','Autonome repositorywerkruimte'):data.orchestrationId?t('Cross-repository orchestration','Orkestratie over repositories'):t('Gitea operations','Gitea-operaties')),el('div',data.capabilityFingerprint?`${t('Capability','Capability')} ${String(data.capabilityFingerprint).slice(0,12)}`:t('Durable Gitea-backed state','Duurzame status uit Gitea'),'muted'));
const top=el('section','','top');top.append(heading,el('span',connected?t('Ready','Gereed'):t('Attention','Actie nodig'),connected?'pill ok':'pill warn'));const grid=el('section','','grid');grid.append(card(t('Repositories','Repositories'),String(data.repositoryCount??summary.total??items.length??0)),card(t('Tools','Tools'),String(data.toolCount??'—')),card(t('Workspace','Werkruimte'),data.workspaceId||data.orchestrationId||'—'),card(t('Next action','Volgende actie'),data.nextAction||t('Inspect and plan','Inspecteren en plannen')));
const detail=el('section','','card');detail.append(el('div',t('Repository state','Repositorystatus'),'label'));const list=el('ul');items.slice(0,25).forEach(item=>{const row=el('li');row.append(el('span',item.repositoryRef||item.name||t('Repository','Repository')),el('span',item.state||(item.healthy===true?t('healthy','gezond'):item.healthy===false?t('attention','actie nodig'):item.branch||''),'muted'));list.append(row)});detail.append(items.length?list:el('div',data.branch?`${data.repositoryRef} · ${data.branch}`:t('No repository rows in this snapshot.','Geen repositoryregels in deze momentopname.'),'empty'));
const actions=el('section','','actions');const refresh=el('button',t('Ask ChatGPT to refresh','ChatGPT laten vernieuwen'));refresh.dataset.action='refresh';refresh.onclick=askRefresh;actions.append(refresh);root.replaceChildren(top,grid,detail,actions);window.openai?.setWidgetState?.({fingerprint:data.capabilityFingerprint||null,workspaceId:data.workspaceId||null,orchestrationId:data.orchestrationId||null});
}
async function askRefresh(){const button=document.querySelector('[data-action="refresh"]');if(button)button.disabled=true;const prompt=t('Refresh the current ITWorx Gitea operations or workspace status and summarize only material changes.','Vernieuw de huidige ITWorx Gitea-operatie- of werkruimtestatus en vat alleen wezenlijke wijzigingen samen.');try{try{await request('ui/message',{role:'user',content:[{type:'text',text:prompt}]})}catch(bridgeError){if(!window.openai?.sendFollowUpMessage)throw bridgeError;await window.openai.sendFollowUpMessage({prompt})}}catch(error){root.append(el('div',`${t('Refresh request failed','Vernieuwingsverzoek mislukt')}: ${error?.message||error}`,'error'))}finally{if(button)button.disabled=false}}
window.addEventListener('message',event=>{if(event.source!==window.parent)return;const message=event.data;if(!message||message.jsonrpc!=='2.0')return;if(message.id!==undefined&&pending.has(message.id)){const item=pending.get(message.id);pending.delete(message.id);clearTimeout(item.timeout);message.error?item.reject(message.error):item.resolve(message.result);return}if(message.method==='ui/notifications/tool-result')render(message.params?.structuredContent)},{passive:true});window.addEventListener('openai:set_globals',event=>{const globals=event.detail?.globals??event.detail??{};setTheme(globals.theme);if(globals.toolOutput!=null)render(globals.toolOutput)});setTheme(window.openai?.theme);let hydrationAttempts=0;const hydrate=()=>{const output=window.openai?.toolOutput;if(output!=null){render(output);return}hydrationAttempts+=1;if(hydrationAttempts<50){setTimeout(hydrate,100);return}render(null)};hydrate();
})();
</script></body></html>"""