chore: keep widget sources only

This commit is contained in:
2026-09-17 23:29:09 +03:00
commit e0404617d2
125 changed files with 8935 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,109 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SHD MCP</title>
<style data-shd-widget-theme="shared">
__SHD_WIDGET_THEME_CSS__
</style>
<style data-shd-widget-write-actions="css">
__SHD_WIDGET_ACTIONS_CSS__
</style>
</head>
<body>
<main class="shell" data-shd-widget-engine="shd-universal-v1" data-widget-module="__SHD_WIDGET_MODULE__"><section class="card">
<div class="eyebrow">SHD MCP</div><h1 id="title">SHD</h1><p class="lede" id="desc"></p><div class="summary" id="summary"></div>
<div class="toolbar"><input class="search" id="search" type="search" autocomplete="off"><button class="refresh" id="refresh" type="button"></button></div>
<div class="status" id="status" role="status" aria-live="polite"></div>
<div class="table-wrap"><table><thead><tr><th id="head-title"></th><th id="head-status"></th><th id="head-date"></th><th id="head-value"></th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty" hidden></div></div><div class="visual" id="visual" hidden></div>
<section class="details" id="details" hidden aria-live="polite"><div class="detail-head"><div><h2 id="detail-title"></h2><div class="sub" id="detail-sub"></div></div><button class="close" id="close" type="button">×</button></div><div class="grid" id="grid"></div></section>
</section></main>
<script>
(function(){
'use strict';
var DEFAULT_MODULE='__SHD_WIDGET_MODULE__';
var C={ru:{refresh:'Обновить',search:'Поиск по названию, статусу или ID',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',status:'Статус',date:'Дата',value:'Детали',id:'ID',details:'Детали',close:'Закрыть',load:'Не удалось загрузить данные.',detail:'Не удалось загрузить детали.',context:'Для обновления нужен контекст источника',total:'Всего',amount:'Сумма',planned:'План',capacity:'Ёмкость',utilization:'Загрузка',overload:'Перегрузка',warnings:'Предупреждения',complete:'Готово',pending:'Ожидают',approved:'Согласовано',rejected:'Отклонено',progress:'Прогресс',score:'Оценка',member:'Участник',role:'Роль',permissions:'Разрешения',preview:'Предпросмотр',noWarnings:'Предупреждений нет.',health:'Состояние',yes:'Да',no:'Нет'},en:{refresh:'Refresh',search:'Search by name, status or ID',loading:'Loading…',empty:'No data to display.',records:'records',status:'Status',date:'Date',value:'Details',id:'ID',details:'Details',close:'Close',load:'Could not load data.',detail:'Could not load details.',context:'A source context is required to refresh.',total:'Total',amount:'Amount',planned:'Planned',capacity:'Capacity',utilization:'Utilization',overload:'Overload',warnings:'Warnings',complete:'Complete',pending:'Pending',approved:'Approved',rejected:'Rejected',progress:'Progress',score:'Score',member:'Member',role:'Role',permissions:'Permissions',preview:'Preview',noWarnings:'No warnings.',health:'Health',yes:'Yes',no:'No'}};
var FIELD_LABELS={ru:{id:'ID',key:'Ключ',project_id:'ID проекта',task_project_id:'ID проекта задачи',linked_project_ids:'Связанные проекты',project_code:'Код проекта',base_reference:'Ссылка на базу',base_public_id:'Публичный ID базы',table_public_id:'Публичный ID таблицы',record_public_id:'Публичный ID записи',title:'Название',name:'Название',description:'Описание',status:'Статус',status_id:'ID статуса',is_done:'Завершено',is_complete:'Завершено',section:'Раздел',sort_order:'Порядок сортировки',created_at:'Создано',updated_at:'Обновлено',due_date:'Срок',deadline:'Дедлайн',starts_at:'Начало',ends_at:'Окончание',url:'Ссылка',diagnostic:'Диагностика',message:'Сообщение',type:'Тип',state:'Состояние',phase:'Этап',health:'Состояние',email:'Электронная почта',username:'Имя пользователя',role:'Роль',permissions:'Разрешения',source:'Источник',module:'Модуль',table:'Таблица',fields:'Поля',value:'Значение',stage:'Стадия',approval_status:'Статус согласования',approval_state:'Состояние согласования',priority:'Приоритет',assignee:'Исполнитель',owner:'Владелец',type:'Тип',category:'Категория',permissions:'Разрешения',permission:'Разрешение',read:'Чтение',write:'Запись',manage:'Управление',admin:'Администратор',view:'Просмотр',create:'Создание',update:'Изменение',delete:'Удаление',error:'Ошибка',warning:'Предупреждение',message:'Сообщение',path:'Путь',link:'Ссылка',amount:'Сумма',total:'Итого',quantity:'Количество',available_quantity:'Доступное количество',stock:'Остаток',planned:'План',planned_hours:'Плановые часы',capacity:'Ёмкость',capacity_hours:'Часы ёмкости',utilization:'Загрузка',utilization_percent:'Загрузка, %',overload:'Перегрузка',overload_hours:'Часы перегрузки',score:'Оценка',progress:'Прогресс',completeness_score:'Оценка полноты',completion_percent:'Полнота, %',availability_percent:'Доступность, %',record_count:'Количество записей',revision:'Ревизия',pages:'Страницы',page_count:'Количество страниц',date:'Дата',is_archived:'В архиве',low_stock:'Мало на складе',critical:'Критический',meta:'Метаданные'},en:{id:'ID',key:'Key',project_id:'Project ID',task_project_id:'Task project ID',linked_project_ids:'Linked projects',project_code:'Project code',base_reference:'Base reference',base_public_id:'Public base ID',table_public_id:'Public table ID',record_public_id:'Public record ID',title:'Title',name:'Name',description:'Description',status:'Status',status_id:'Status ID',is_done:'Completed',is_complete:'Completed',section:'Section',sort_order:'Sort order',created_at:'Created',updated_at:'Updated',due_date:'Due date',deadline:'Deadline',starts_at:'Starts',ends_at:'Ends',url:'URL',diagnostic:'Diagnostics',message:'Message',type:'Type',state:'State',phase:'Phase',health:'Health',email:'Email',username:'Username',role:'Role',permissions:'Permissions',source:'Source',module:'Module',table:'Table',fields:'Fields',value:'Value',stage:'Stage',approval_status:'Approval status',approval_state:'Approval state',priority:'Priority',assignee:'Assignee',owner:'Owner',type:'Type',category:'Category',permissions:'Permissions',permission:'Permission',read:'Read',write:'Write',manage:'Manage',admin:'Admin',view:'View',create:'Create',update:'Update',delete:'Delete',error:'Error',warning:'Warning',message:'Message',path:'Path',link:'Link',amount:'Amount',total:'Total',quantity:'Quantity',available_quantity:'Available quantity',stock:'Stock',planned:'Planned',planned_hours:'Planned hours',capacity:'Capacity',capacity_hours:'Capacity hours',utilization:'Utilization',utilization_percent:'Utilization, %',overload:'Overload',overload_hours:'Overload hours',score:'Score',progress:'Progress',completeness_score:'Completeness score',completion_percent:'Completeness, %',availability_percent:'Availability, %',record_count:'Record count',revision:'Revision',pages:'Pages',page_count:'Page count',date:'Date',is_archived:'Archived',low_stock:'Low stock',critical:'Critical',meta:'Metadata'}};
var VALUE_LABELS={ru:{Tasks_Status_Todo:'К выполнению',Tasks_Status_Done:'Завершено',Tasks_Status_In_Progress:'В работе',Tasks_Status_Review:'На проверке',Modules_Done:'Завершено',todo:'К выполнению',to_do:'К выполнению','to do':'К выполнению',in_progress:'В работе','in progress':'В работе','in-progress':'В работе',in_progress_status:'В работе',done:'Завершено',completed:'Завершено',complete:'Завершено',closed:'Закрыто',active:'Активно',pending:'Ожидает',review:'На проверке',approved:'Согласовано',rejected:'Отклонено',archived:'В архиве',not_set:'Не задано'},en:{Tasks_Status_Todo:'To do',Tasks_Status_Done:'Done',Tasks_Status_In_Progress:'In progress',Tasks_Status_Review:'In review',Modules_Done:'Done',todo:'To do',to_do:'To do','to do':'To do',in_progress:'In progress','in progress':'In progress','in-progress':'In progress',in_progress_status:'In progress',done:'Done',completed:'Completed',complete:'Complete',closed:'Closed',active:'Active',pending:'Pending',review:'In review',approved:'Approved',rejected:'Rejected',archived:'Archived',not_set:'Not set'}};
var MODULE_CONFIG=__SHD_WIDGET_CONFIG__;
var M=MODULE_CONFIG.copy;
var S=MODULE_CONFIG.defaults;
var pending=new Map(),nextId=1,rows=[],state={module:DEFAULT_MODULE,config:Object.assign({},S[DEFAULT_MODULE]),lang:'ru',timezone:'',busy:false};
var e={title:document.getElementById('title'),desc:document.getElementById('desc'),search:document.getElementById('search'),refresh:document.getElementById('refresh'),status:document.getElementById('status'),summary:document.getElementById('summary'),tableWrap:document.querySelector('.table-wrap'),visual:document.getElementById('visual'),rows:document.getElementById('rows'),empty:document.getElementById('empty'),details:document.getElementById('details'),detailTitle:document.getElementById('detail-title'),detailSub:document.getElementById('detail-sub'),grid:document.getElementById('grid'),close:document.getElementById('close'),ht:document.getElementById('head-title'),hs:document.getElementById('head-status'),hd:document.getElementById('head-date'),hv:document.getElementById('head-value')};
function lang(){var x=typeof window!=='undefined'&&window.openai&&window.openai.locale;x=String(x||document.documentElement.lang||'ru').toLowerCase();return x.indexOf('en')===0?'en':'ru'}
function t(k){return(C[state.lang]||C.ru)[k]||k}
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
function merge(a,b){return Object.assign({},a||{},b||{})}
function payload(x){if(!x)return null;if(Object.prototype.hasOwnProperty.call(x,'structuredContent'))return x.structuredContent;if(Object.prototype.hasOwnProperty.call(x,'toolOutput'))return x.toolOutput;return x}
function unwrap(x){if(x&&x.isError)throw Error(t('load'));var p=payload(x)||{};if(p.error)throw Error(String(p.error.message||p.error.detail||p.error));if(p.ok===false&&p.message)throw Error(String(p.message));return p}
function applyConfig(p){p=payload(p)||{};var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{},mod=String(w.module||m.module||state.module||DEFAULT_MODULE);state.module=M[mod]?mod:DEFAULT_MODULE;state.config=merge(S[state.module],w);if(!obj(state.config.sourceArgs)&&obj(m.sourceArgs))state.config.sourceArgs=m.sourceArgs;state.lang=lang();labels()}
function labels(){var m=(M[state.module]||M.discussions)[state.lang]||M.discussions.ru;document.documentElement.lang=state.lang;e.title.textContent=m[0];e.desc.textContent=m[1];e.search.placeholder=t('search');e.refresh.textContent=t('refresh');e.empty.textContent=t('empty');e.close.setAttribute('aria-label',t('close'));e.ht.textContent=m[2];e.hs.textContent=t('status');e.hd.textContent=m[3];e.hv.textContent=m[4]}
function req(method,params){var o=typeof window!=='undefined'&&window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id='widget-'+nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=window.setTimeout(function(){pending.delete(id);reject(Error(t('load')))},12000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
function notify(method,params){window.parent.postMessage({jsonrpc:'2.0',method:method,params:params||{}},'*')}
function path(x,p){return String(p||'').split('.').reduce(function(v,k){return v&&typeof v==='object'?v[k]:undefined},x)}
function findArray(x,d){if(Array.isArray(x))return x;if(!obj(x)||d>2)return null;var keys=['items','records','bookings','upcoming_bookings','topics','notifications','pages','spaces','controllers','agents','user_agents','tables','monitors','data'];for(var i=0;i<keys.length;i++)if(Array.isArray(x[keys[i]]))return x[keys[i]];for(var k in x)if(Object.prototype.hasOwnProperty.call(x,k)){var a=findArray(x[k],d+1);if(a)return a}return null}
function flatten(a){var n=state.config.nested||state.config.nestedCollection;if(!n)return a;var out=[];a.forEach(function(parent){if(parent&&Array.isArray(parent[n]))parent[n].forEach(function(child){var x=Object.assign({},child);if(parent.name&&!x.group_name)x.group_name=parent.name;out.push(x)});else out.push(parent)});return out}
function list(x){var p=payload(x)||{};if(Array.isArray(p.items))return p.items;var ps=Array.isArray(state.config.paths)?state.config.paths:(Array.isArray(state.config.arrayPaths)?state.config.arrayPaths:[]),combined=[];for(var i=0;i<ps.length;i++){var c=path(p,ps[i]),a=Array.isArray(c)?c:findArray(c,0);if(a){if(state.config.combinePaths)combined=combined.concat(a);else return flatten(a)}}if(state.config.combinePaths&&combined.length)return flatten(combined);var d=p.data===undefined?p:p.data;return flatten(Array.isArray(d)?d:(findArray(d,0)||[]))}
function str(x){if(x===null||x===undefined||x==='')return'';if(typeof x==='object'){try{return JSON.stringify(x)}catch(err){return''}}return String(x)}
function id(x,i){var keys=['id','key','code','uuid','page_id','topic_id','booking_id','item_id','monitor_id','table'];for(var n=0;n<keys.length;n++)if(x&&x[keys[n]]!==undefined&&x[keys[n]]!==null&&x[keys[n]]!=='')return str(x[keys[n]]);return String(i+1)}
function status(x){var v=x&&x.status;if(v&&typeof v==='object')v=v.name||v.label||v.code||v.id;return str(v||(x&&(x.state||x.phase||x.health))||'')}
function title(x,i){return str(x&&(x.title||x.name||x.subject||x.display_name||x.label||x.table_name||x.table||x.key))||(t('records')+' '+(i+1))}
function date(x){return str(x&&(x.starts_at||x.start_at||x.due_date||x.checked_at||x.updated_at||x.created_at||x.sent_at||x.published_at||x.date))}
function value(x){if(!x)return'';if(x.availability_percent!==undefined&&x.availability_percent!==null)return str(x.availability_percent)+'%';if(x.quantity!==undefined&&x.quantity!==null)return str(x.quantity);if(x.record_count!==undefined&&x.record_count!==null)return str(x.record_count);return str(x.value!==undefined?x.value:(x.guest_name||x.space_name||x.group_name||x.controller_type||x.type||x.source||x.module||''))}
function sub(x){return str(x&&(x.subtitle||x.project_code||x.space_name||x.channel_name||x.group_name||x.code||x.email))}
function normalize(x,i){var y=obj(x)?Object.assign({},x):{value:x};y.id=id(x,i);y.title=title(x,i);y.subtitle=sub(x);y.status=status(x);y.date=date(x);y.value=value(x);return y}
function dateOnlyText(raw){var parts=raw.split('-').map(Number);if(parts.length!==3||parts.some(function(part){return!Number.isFinite(part)}))return null;var date=new Date(Date.UTC(parts[0],parts[1]-1,parts[2],12));try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeZone:'UTC'}).format(date)}catch(err){return null}}
function dateText(x){if(x===null||x===undefined||x==='')return'—';var n;if(x instanceof Date)n=x.getTime();else if(typeof x==='number')n=Math.abs(x)<100000000000?x*1000:x;else{var raw=String(x).trim();if(/^\d{4}-\d{2}-\d{2}$/.test(raw)){var only=dateOnlyText(raw);if(only)return only}var numeric=Number(raw);n=raw!==''&&Number.isFinite(numeric)?(Math.abs(numeric)<100000000000?numeric*1000:numeric):Date.parse(raw);if(Number.isNaN(n)&&raw)n=Date.parse(raw.replace(/([+-]\d{2}):(\d{2})$/,'$1$2'))}if(!Number.isFinite(n))return String(x);var options={dateStyle:'medium',timeStyle:'medium'};if(state.timezone)options.timeZone=state.timezone;try{return new Intl.DateTimeFormat(state.lang,options).format(new Date(n))}catch(err){return String(x)}}
function make(tag,v,cls){var n=document.createElement(tag);if(cls)n.className=cls;if(v!==undefined)n.textContent=v;return n}
function fieldLabel(key){var raw=String(key||'');var labels=FIELD_LABELS[state.lang]||FIELD_LABELS.ru;return labels[raw]||raw.replace(/[_-]+/g,' ')}
function dateKey(key){return['date','time','timestamp','source_timestamp','from','to','start','end','bucket_start','created_at','updated_at','due_date','deadline','starts_at','ends_at','checked_at','sent_at','published_at'].indexOf(String(key||''))!==-1}
function detailText(x,key){if(x===null||x===undefined||x==='')return'—';if(typeof x==='boolean')return x?t('yes'):t('no');if(Array.isArray(x))return'['+x.map(function(v){return detailText(v,key)}).join(', ')+']';if(obj(x))return'{'+Object.keys(x).map(function(k){return fieldLabel(k)+': '+detailText(x[k],k)}).join(', ')+'}';var raw=String(x),labels=VALUE_LABELS[state.lang]||VALUE_LABELS.ru;if(dateKey(key)&&!Number.isNaN(Date.parse(raw)))return dateText(raw);return labels[raw]||labels[raw.toLowerCase()]||raw}
function renderSummary(){e.summary.replaceChildren();var n=make('span');n.append(make('strong',String(rows.length)),' '+t('records'));e.summary.append(n);var c={};rows.forEach(function(x){if(x.status)c[x.status]=(c[x.status]||0)+1});Object.keys(c).slice(0,3).forEach(function(k){var n=make('span');n.append(make('strong',String(c[k])),' '+detailText(k,'status'));e.summary.append(n)})}
function field(x,keys){for(var i=0;i<keys.length;i++){var v=x&&x[keys[i]];if(v!==undefined&&v!==null&&v!=='')return v}return''}
function numeric(x,keys){var v=field(x,keys);if(v===null||v===undefined||v==='')return null;if(typeof v==='number')return Number.isFinite(v)?v:null;var n=Number(String(v).replace(/,/g,'.').replace(/[^0-9.-]/g,''));return Number.isFinite(n)?n:null}
function groupValue(x,key){var v=key?field(x,[key]):status(x);if(obj(v))v=v.name||v.label||v.code||v.id;return str(v)||'—'}
function recordAttrs(node,x){if(!node||!x)return node;var recordId=str(x.id);if(recordId)node.dataset.shdRecordId=recordId;var recordTitle=str(x.title);if(recordTitle)node.dataset.shdRecordTitle=recordTitle;return node}
function interactiveRecord(node,x){recordAttrs(node,x);if(node.tagName!=='BUTTON'&&node.tagName!=='A'){node.classList.add('record');node.tabIndex=0;node.setAttribute('role','button');node.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}}}return node}
function visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';recordAttrs(b,x);b.append(make('span',x.title||'SHD','visual-title'));if(x.subtitle)b.append(make('span',x.subtitle,'sub'));var line=make('span',undefined,'visual-line');line.append(make('span',detailText(x.status,'status'),'pill'));if(x.date)line.append(make('span',dateText(x.date),'visual-date'));b.append(line);var v=field(x,['total_amount','amount','planned_hours','utilization_percent','score','quantity','revision','pages','value']);if(v!==''&&v!==null)b.append(make('span',detailText(v),'visual-value'));b.onclick=function(){details(x)};return b}
function visualEmpty(){e.visual.replaceChildren(make('div',t('empty'),'empty'))}
function renderKanban(a){if(!a.length){visualEmpty();return}var groups={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'status');(groups[g]||(groups[g]=[])).push(x)});var board=make('div',undefined,'kanban');Object.keys(groups).forEach(function(g){var col=make('section',undefined,'kanban-col');var head=make('div',undefined,'kanban-head');head.append(make('span',detailText(g,state.config.groupBy||'status')),make('span',String(groups[g].length),'pill'));col.append(head);var list=make('div',undefined,'kanban-list');groups[g].forEach(function(x){list.append(visualCard(x))});col.append(list);board.append(col)});e.visual.append(board)}
function renderTimeline(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'timeline');a.slice().sort(function(x,y){return String(x.date||'').localeCompare(String(y.date||''))}).forEach(function(x){var item=make('article',undefined,'timeline-item'),rail=make('span',undefined,'timeline-rail'),dot=make('span',undefined,'timeline-dot'),content=make('div',undefined,'timeline-content');content.append(make('div',dateText(x.date),'timeline-meta'),visualCard(x,'timeline-card'));item.append(rail,dot,content);wrap.append(item)});e.visual.append(wrap)}
function renderFunnel(a){if(!a.length){visualEmpty();return}var groups={},amounts={};a.forEach(function(x){var g=groupValue(x,state.config.groupBy||'stage');groups[g]=(groups[g]||0)+1;amounts[g]=(amounts[g]||0)+(numeric(x,['total_amount','amount','value','sum'])||0)});var max=Object.keys(groups).reduce(function(current,g){return Math.max(current,groups[g])},0),wrap=make('div',undefined,'funnel');Object.keys(groups).forEach(function(g){var row=make('div',undefined,'funnel-row'),bar=document.createElement('progress');bar.className='funnel-fill';bar.max=max||1;bar.value=groups[g];row.append(make('span',detailText(g,state.config.groupBy||'stage')),bar,make('span',String(groups[g])+(amounts[g]?' · '+String(amounts[g]):'')));wrap.append(row)});e.visual.append(wrap)}
function metric(label,value){var d=make('div',undefined,'metric');d.append(make('span',label,'metric-label'),make('span',value===null||value===undefined?'—':str(value),'metric-value'));return d}
function renderDashboard(a,mode){var grid=make('div',undefined,'metric-grid'),total=a.length,amount=0,planned=0,capacity=0,util=0,overload=0;a.forEach(function(x){amount+=numeric(x,['total_amount','amount','total','sum','value'])||0;planned+=numeric(x,['planned_hours','planned','hours'])||0;capacity+=numeric(x,['capacity_hours','capacity'])||0;util+=numeric(x,['utilization_percent','utilization'])||0;overload+=numeric(x,['overload_hours','overload'])||0});grid.append(metric(t('total'),total));if(mode==='workload'){grid.append(metric(t('planned'),planned||null),metric(t('capacity'),capacity||null),metric(t('utilization'),a.length?Math.round(util/a.length)+'%':null),metric(t('overload'),overload||0))}else{grid.append(metric(t('amount'),amount||null),metric(t('status'),Object.keys(a.reduce(function(m,x){m[groupValue(x,'status')]=1;return m},{})).length))}e.visual.append(grid);if(!a.length){visualEmpty();return}var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
function approvalValue(x){return groupValue(x,'approval_status')!=='—'?groupValue(x,'approval_status'):groupValue(x,'approval_state')!=='—'?groupValue(x,'approval_state'):groupValue(x,'status')}
function renderStatusSummary(a,keys){if(!a.length){visualEmpty();return}var counts={};a.forEach(function(x){var g=groupValue(x,keys[0]);if(g==='—')g=groupValue(x,keys[1]);if(g==='—')g=groupValue(x,'status');counts[g]=(counts[g]||0)+1});var grid=make('div',undefined,'metric-grid');Object.keys(counts).slice(0,6).forEach(function(g){grid.append(metric(detailText(g,keys[0]),counts[g]))});e.visual.append(grid);var list=make('div',undefined,'kanban-list');a.slice(0,60).forEach(function(x){list.append(visualCard(x))});e.visual.append(list)}
function progressValue(x){var n=numeric(x,['score','completeness_score','completion_percent','progress','availability_percent']);if(n!==null)return Math.max(0,Math.min(100,n));if(x.complete===true||x.is_complete===true||String(status(x)).toLowerCase()==='complete')return 100;return 0}
function renderProgress(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=interactiveRecord(make('article',undefined,'progress-card'),x);card.append(make('strong',x.title||'SHD','visual-title'));var meta=make('span',x.subtitle||dateText(x.date),'visual-date');card.append(meta);var p=document.createElement('progress');p.className='progress';p.max=100;p.value=progressValue(x);card.append(p,make('span',t('progress')+': '+String(p.value)+'%','visual-value'));card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
function isWarning(x){var s=String(status(x)).toLowerCase();if(x.low_stock||x.is_low_stock||x.critical||x.warning)return true;if(/low|critical|out|error|overdue|maintenance/.test(s))return true;var q=numeric(x,['quantity','available_quantity','stock']);var min=numeric(x,['min_quantity','minimum_quantity','reorder_level']);return q!==null&&min!==null&&q<=min}
function renderWarnings(a){var warnings=a.filter(isWarning),grid=make('div',undefined,'metric-grid');grid.append(metric(t('warnings'),warnings.length),metric(t('total'),a.length));e.visual.append(grid);if(!warnings.length){e.visual.append(make('div',t('noWarnings'),'empty'));return}var list=make('div',undefined,'kanban-list');warnings.forEach(function(x){list.append(visualCard(x,'warning'))});e.visual.append(list)}
function renderCalendar(a){if(!a.length){visualEmpty();return}var days={};a.forEach(function(x){var d=x.date?String(x.date).slice(0,10):'—';(days[d]||(days[d]=[])).push(x)});var cal=make('div',undefined,'calendar');Object.keys(days).forEach(function(d){var day=make('section',undefined,'calendar-day'),label=/^\d{4}-\d{2}-\d{2}$/.test(d)?dateOnlyText(d):d;day.append(make('div',label||d,'calendar-head'));days[d].forEach(function(x){day.append(visualCard(x))});cal.append(day)});e.visual.append(cal)}
function memberName(x){var v=field(x,['member_name','username','email','name','display_name','user']);if(obj(v))return str(v.name||v.username||v.email||v.id);return str(v)||x.id}
function permissionText(x){var p=x.permissions||x.permission||x.scopes||'';if(obj(p))return Object.keys(p).filter(function(k){return p[k]===true||p[k]===1||typeof p[k]==='string'}).map(function(k){return fieldLabel(k)+(typeof p[k]==='string'?'='+detailText(p[k],k):'')}).join(', ')||'—';if(Array.isArray(p))return p.map(function(k){return detailText(k,'permissions')}).join(', ')||'—';return detailText(p,'permissions')||'—'}
function renderMatrix(a){if(!a.length){visualEmpty();return}var wrap=make('div',undefined,'matrix-wrap'),table=make('table',undefined,'matrix-table'),head=make('tr');[t('member'),t('role'),t('status'),t('permissions')].forEach(function(x){head.append(make('th',x))});table.append(make('thead').appendChild(head).parentNode);var body=make('tbody');a.forEach(function(x){var row=interactiveRecord(make('tr'),x);row.append(make('td',memberName(x)),make('td',detailText(field(x,['role','organization_role','type']),'role')||'—'),make('td',detailText(status(x),'status')||'—'),make('td',permissionText(x)));row.onclick=function(){details(x)};body.append(row)});table.append(body);wrap.append(table);e.visual.append(wrap)}
function treeId(x,i){return str(field(x,['id','space_id','page_id','collection_id','key']))||String(i)}
function treeParent(x){return str(field(x,['parent_id','parentId','parent_space_id','parent_collection_id']))}
function renderTree(a){if(!a.length){visualEmpty();return}var by={};a.forEach(function(x,i){var p=treeParent(x);(by[p]||(by[p]=[])).push({x:x,i:i})});var tree=make('div',undefined,'tree');function branch(parent,host,seen){(by[parent]||[]).forEach(function(entry){var id=treeId(entry.x,entry.i);if(seen[id])return;var wrap=make('div',undefined,'tree-node');wrap.append(visualCard(entry.x));host.append(wrap);var child=make('div',undefined,'tree-children');branch(id,child,Object.assign({},seen,(function(){var o={};o[id]=true;return o})()));if(child.childNodes.length)wrap.append(child)})}branch('',tree,{});if(!tree.childNodes.length)a.forEach(function(x){tree.append(visualCard(x))});e.visual.append(tree)}
function renderPreview(a){if(!a.length){visualEmpty();return}var grid=make('div',undefined,'preview-grid');a.forEach(function(x){var card=interactiveRecord(make('article',undefined,'preview-card'),x);card.append(make('strong',x.title||'SHD','visual-title'));var fields=make('div',undefined,'preview-fields');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,10).forEach(function(k){var cell=make('div',undefined,'preview-field');cell.append(make('strong',fieldLabel(k)+': '),make('span',detailText(x[k],k)));fields.append(cell)});card.append(fields);card.onclick=function(){details(x)};grid.append(card)});e.visual.append(grid)}
function renderVisual(a){e.visual.replaceChildren();var v=state.config.view||'table';if(v==='kanban')renderKanban(a);else if(v==='workload'||v==='dashboard')renderDashboard(a,v);else if(v==='timeline')renderTimeline(a);else if(v==='funnel')renderFunnel(a);else if(v==='approval')renderStatusSummary(a,['approval_status','approval_state']);else if(v==='completeness'||v==='progress')renderProgress(a);else if(v==='health')renderStatusSummary(a,['health','status']);else if(v==='calendar')renderCalendar(a);else if(v==='matrix')renderMatrix(a);else if(v==='warnings')renderWarnings(a);else if(v==='tree')renderTree(a);else if(v==='preview')renderPreview(a);else renderDashboard(a,'dashboard')}
function renderTable(a){e.rows.replaceChildren();e.empty.hidden=a.length!==0;a.forEach(function(x){var r=document.createElement('tr');r.className='record';recordAttrs(r,x);r.tabIndex=0;r.setAttribute('role','button');r.setAttribute('aria-label',x.title);var n=make('td');n.append(make('span',x.title,'title'));if(x.subtitle)n.append(make('span',x.subtitle,'sub'));var s=make('span',detailText(x.status,'status'),'pill');if(x.status)s.classList.add(String(x.status).toLowerCase().replace(/[^a-z0-9_-]+/g,'-'));var sc=make('td');sc.append(s);r.append(n,sc,make('td',dateText(x.date)),make('td',detailText(x.value,'value')));r.onclick=function(){details(x)};r.onkeydown=function(ev){if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();details(x)}};e.rows.append(r)});renderSummary()}
function renderRows(){var q=e.search.value.trim().toLowerCase(),a=rows.filter(function(x){return!q||[x.title,x.subtitle,x.status,x.id,x.value].map(str).join(' ').toLowerCase().indexOf(q)!==-1});if(state.config.view&&state.config.view!=='table'){e.tableWrap.hidden=true;e.visual.hidden=false;e.empty.hidden=true;renderVisual(a);renderSummary();return}e.tableWrap.hidden=false;e.visual.hidden=true;renderTable(a)}
function setTimezone(p){var source=payload(p)||{},data=source.data!==undefined?source.data:source,timezone=data&&data.timezone||source.timezone;if(typeof timezone==='string'&&timezone)state.timezone=timezone}
function render(x){var p=unwrap(x);applyConfig(p);setTimezone(p);rows=list(p).map(normalize);var dir=state.config.sort==='asc'?1:-1;rows.sort(function(a,b){var x=Date.parse(a.date),y=Date.parse(b.date);if(!Number.isNaN(x)&&!Number.isNaN(y)&&x!==y)return(x-y)*dir;return a.title.localeCompare(b.title,state.lang)*dir});window.__SHD_WIDGET_STATE__=p;renderRows();window.dispatchEvent(new CustomEvent('shd-widget-data',{detail:p}))}
function setStatus(x,bad){e.status.textContent=x||'';e.status.className=bad?'status error':'status'}
function add(k,v,key){var d=make('div',undefined,'detail');d.append(make('span',k,'label'),make('span',detailText(v,key),'value'));e.grid.append(d)}
function show(x){e.detailTitle.textContent=x.title||'SHD';e.detailSub.textContent=x.subtitle||x.id||'';e.grid.replaceChildren();add(t('id'),x.id,'id');if(x.status)add(t('status'),x.status,'status');if(x.date)add(t('date'),dateText(x.date),'date');if(x.value)add(t('value'),x.value,'value');Object.keys(x).filter(function(k){return['id','title','subtitle','status','date','value','meta'].indexOf(k)===-1}).slice(0,8).forEach(function(k){add(fieldLabel(k),x[k],k)});e.details.hidden=false}
function details(x){show(x);if(!state.config.readTool)return;var k=state.config.detailKey;if(!k)return;var v=x[k]===undefined?x.id:x[k];if(v===undefined||v===null||v==='')return;if(state.module==='status-page'&&!/^\d+$/.test(String(v)))return;var a={};if(Array.isArray(state.config.detailSourceArgs)){var ctx=merge(state.config.listArgs,state.config.sourceArgs);state.config.detailSourceArgs.forEach(function(name){if(ctx[name]!==undefined)a[name]=ctx[name]})}else a=merge(state.config.sourceArgs,state.config.detailArgs);a=merge(a,state.config.detailArgs);a[k]=k==='table'?String(v):(Number(v)||v);req('tools/call',{name:state.config.readTool,arguments:a}).then(function(result){var p=unwrap(result),d=p.data===undefined?p:p.data;show(x);if(Array.isArray(d)){add(t('records'),d.length);d.slice(0,6).forEach(function(q,i){add(String(i+1),q&&(q.message||q.body||q.content||q.title||q))})}else if(obj(d))Object.keys(d).slice(0,12).forEach(function(q){add(fieldLabel(q),d[q],q)});else add(t('details'),d)}).catch(function(err){setStatus(err.message||t('detail'),true)})}
function contextReady(a){if(!state.config.contextRequired)return true;if(Array.isArray(state.config.contextSets)&&state.config.contextSets.length)return state.config.contextSets.every(function(set){return set.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''})});var keys=Array.isArray(state.config.contextKeys)&&state.config.contextKeys.length?state.config.contextKeys:['base','project_code','project'];var ready=state.config.contextAll?keys.every(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''}):keys.some(function(k){return a[k]!==undefined&&a[k]!==null&&a[k]!==''});return ready}
function refresh(){if(state.busy)return;var a=merge(state.config.listArgs,state.config.sourceArgs);if(!contextReady(a)){setStatus(t('context'),true);return}if(!state.config.listTool)return;state.busy=true;e.refresh.disabled=true;setStatus(t('loading'),false);req('tools/call',{name:state.config.listTool,arguments:a}).then(function(x){render(x);setStatus('',false)}).catch(function(err){setStatus(err.message||t('load'),true)}).finally(function(){state.busy=false;e.refresh.disabled=false})}
function init(){return req('ui/initialize',{protocolVersion:'2026-01-26',appInfo:{name:'SHD '+(M[state.module]||M.discussions).ru[0],title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){notify('ui/notifications/initialized',{})}).catch(function(){})}
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data;if(!m||m.jsonrpc!=='2.0')return;if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);window.clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('load')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'&&!rows.length){try{render(m.params||{})}catch(err){setStatus(err.message||t('load'),true)}}if(m.method==='ui/notifications/tool-result'){try{render(m.params&&m.params.structuredContent)}catch(err){setStatus(err.message||t('load'),true)}}},{passive:true});
e.search.oninput=renderRows;e.refresh.onclick=refresh;e.close.onclick=function(){e.details.hidden=true};state.lang=lang();labels();void init();var out=typeof window!=='undefined'&&window.openai?window.openai.toolOutput:null;if(out){try{render(out)}catch(err){setStatus(err.message||t('load'),true)}}else{var input=typeof window!=='undefined'&&window.openai?window.openai.toolInput:null;if(input){try{render(input)}catch(err){setStatus(err.message||t('load'),true)}}else refresh()}
}());
</script>
<script data-shd-widget-write-actions="js">
__SHD_WIDGET_ACTIONS_JS__
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,142 @@
.shd-widget-actions {
min-width: 0;
padding: var(--shd-space-5, 20px);
border: 1px solid var(--shd-line-strong, var(--line, rgba(31, 41, 55, .16)));
border-radius: var(--shd-radius-md, 14px);
background: var(--shd-surface-raised, var(--surface, #e8edf2));
color: var(--shd-ink, var(--ink, #1d1f25));
box-shadow: var(--shd-shadow-soft, none);
}
.shd-widget-actions[hidden] {
display: none;
}
.shd-widget-actions-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--shd-space-3, 12px);
margin-bottom: var(--shd-space-3, 12px);
}
.shd-widget-actions-selection {
overflow: hidden;
color: var(--shd-muted, var(--muted, #616670));
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
.shd-widget-actions-select,
.shd-widget-actions-input {
width: 100%;
padding: 9px 10px;
border: 1px solid var(--shd-line-strong, var(--line, rgba(31, 41, 55, .16)));
border-radius: var(--shd-radius-sm, 10px);
background: var(--shd-bg, var(--bg, #f8fafc));
color: var(--shd-ink, var(--ink, #1d1f25));
}
.shd-widget-actions-select:focus,
.shd-widget-actions-input:focus {
border-color: var(--shd-accent, var(--accent, #535963));
outline: 0;
box-shadow: 0 0 0 3px var(--shd-accent-soft, var(--soft, rgba(75, 85, 99, .12)));
}
.shd-widget-actions-form {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: var(--shd-space-3, 12px);
margin-top: var(--shd-space-3, 12px);
}
.shd-widget-actions-field {
display: grid;
gap: var(--shd-space-1, 4px);
color: var(--shd-muted, var(--muted, #616670));
font-size: 11px;
}
.shd-widget-actions-field:has(textarea) {
grid-column: span 2;
}
.shd-widget-actions-field input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: var(--shd-accent, var(--accent, #535963));
}
.shd-widget-actions-confirm {
display: flex;
grid-column: 1 / -1;
align-items: center;
gap: var(--shd-space-2, 8px);
color: var(--shd-ink, var(--ink, #1d1f25));
font-size: 12px;
}
.shd-widget-actions-warning,
.shd-widget-actions-hint {
grid-column: 1 / -1;
color: var(--shd-muted, var(--muted, #616670));
font-size: 12px;
}
.shd-widget-actions-danger {
color: var(--shd-danger, var(--bad, #b42318));
}
.shd-widget-actions-status {
min-height: 18px;
margin-top: var(--shd-space-3, 12px);
color: var(--shd-muted, var(--muted, #616670));
font-size: 12px;
}
.shd-widget-actions-status-error {
color: var(--shd-danger, var(--bad, #b42318));
}
.shd-widget-actions-buttons {
display: flex;
justify-content: flex-end;
gap: var(--shd-space-2, 8px);
margin-top: var(--shd-space-4, 16px);
}
.shd-widget-actions-buttons button {
padding: 9px 12px;
border: 1px solid var(--shd-line-strong, var(--line, rgba(31, 41, 55, .16)));
border-radius: var(--shd-radius-sm, 10px);
background: var(--shd-surface-subtle, var(--bg, #f8fafc));
color: var(--shd-ink, var(--ink, #1d1f25));
cursor: pointer;
}
.shd-widget-actions-buttons button:hover {
border-color: var(--shd-accent, var(--accent, #535963));
background: var(--shd-surface, var(--hover, #e1e8ef));
}
.shd-widget-actions-buttons button:disabled {
cursor: wait;
opacity: .62;
}
@media (max-width: 600px) {
.shd-widget-actions-field:has(textarea) {
grid-column: auto;
}
.shd-widget-actions-header {
display: block;
}
.shd-widget-actions-selection {
display: block;
margin-top: var(--shd-space-1, 4px);
}
}
@@ -0,0 +1,454 @@
(function () {
'use strict';
if (window.__SHD_WIDGET_WRITE_ACTIONS__) return;
window.__SHD_WIDGET_WRITE_ACTIONS__ = true;
var copy = {
ru: {
actions: 'Действия',
chooseAction: 'Выберите действие',
selectRecord: 'Сначала выберите запись.',
submit: 'Сохранить',
cancel: 'Отмена',
confirm: 'Подтверждаю действие',
warning: 'Проверьте данные перед записью.',
destructive: 'Операция может изменить или удалить данные.',
required: 'Заполните обязательное поле.',
invalidJson: 'Введите корректный JSON.',
success: 'Изменения сохранены.',
failure: 'Не удалось сохранить изменения.',
loading: 'Сохраняю…',
noActions: 'Для этого виджета нет доступных действий.',
refreshed: 'Список обновлён.',
fieldValue: 'Значение',
},
en: {
actions: 'Actions',
chooseAction: 'Choose an action',
selectRecord: 'Select a record first.',
submit: 'Save',
cancel: 'Cancel',
confirm: 'I confirm this action',
warning: 'Review the values before writing.',
destructive: 'This operation may change or remove data.',
required: 'Fill in the required field.',
invalidJson: 'Enter valid JSON.',
success: 'Changes saved.',
failure: 'Could not save changes.',
loading: 'Saving…',
noActions: 'No actions are available for this widget.',
refreshed: 'List refreshed.',
fieldValue: 'Value',
},
};
var state = {
widget: {},
records: [],
selected: null,
action: null,
inputs: {},
busy: false,
lang: String(document.documentElement.lang || 'ru').toLowerCase().indexOf('ru') === 0 ? 'ru' : 'en',
};
var pending = new Map();
var nextRequestId = 1;
var elements = {};
function t(key) {
return (copy[state.lang] && copy[state.lang][key]) || copy.en[key] || key;
}
function object(value) {
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
}
function normalize(value) {
if (!value) return null;
if (value.structuredContent) return normalize(value.structuredContent);
if (value.toolOutput) return normalize(value.toolOutput);
if (value.toolInput) return normalize(value.toolInput);
if (value.arguments && !value.data && !value.items && !value.projects) return normalize(value.arguments);
return value;
}
function widgetFrom(value) {
var payload = normalize(value) || {};
var meta = object(payload.meta) ? payload.meta : {};
return object(meta.widget) ? meta.widget : {};
}
function recordsFrom(value) {
var payload = normalize(value) || {};
var data = Array.isArray(payload.items) ? payload.items : payload.projects;
if (!Array.isArray(data)) data = payload.data;
if (data && !Array.isArray(data) && Array.isArray(data.data)) data = data.data;
return Array.isArray(data) ? data.filter(function (item) { return object(item); }) : [];
}
function readPath(value, path) {
if (!value || path === undefined || path === null) return undefined;
var parts = String(path).split('.');
var current = value;
for (var index = 0; index < parts.length; index += 1) {
if (current === undefined || current === null) return undefined;
current = current[parts[index]];
}
return current;
}
function firstValue(paths) {
var list = Array.isArray(paths) ? paths : [paths];
var sources = [state.selected, state.widget.sourceArgs, state.widget.listArgs];
for (var sourceIndex = 0; sourceIndex < sources.length; sourceIndex += 1) {
var sourceObject = sources[sourceIndex];
if (!sourceObject) continue;
for (var pathIndex = 0; pathIndex < list.length; pathIndex += 1) {
var value = readPath(sourceObject, list[pathIndex]);
if (value !== undefined && value !== null && value !== '') return value;
}
}
return undefined;
}
function text(value) {
if (value === undefined || value === null) return '';
if (typeof value === 'object') return JSON.stringify(value);
return String(value);
}
function label(value) {
if (object(value)) return value[state.lang] || value.ru || value.en || t('fieldValue');
return String(value || t('fieldValue'));
}
function actionLabel(action) {
return label(action && action.label) || action.id || action.tool || t('actions');
}
function hasTargets(action) {
return Boolean(action && ((Array.isArray(action.targets) && action.targets.length) || action.target));
}
function actionList() {
return Array.isArray(state.widget.actions) ? state.widget.actions.filter(function (action) {
return action && action.tool;
}) : [];
}
function request(method, params) {
var openai = typeof window !== 'undefined' ? window.openai : undefined;
if (method === 'tools/call' && openai && typeof openai.callTool === 'function') {
return openai.callTool(params.name, params.arguments || {});
}
var id = 'actions-' + nextRequestId++;
window.parent.postMessage({ jsonrpc: '2.0', id: id, method: method, params: params || {} }, '*');
return new Promise(function (resolve, reject) {
var timeout = window.setTimeout(function () {
pending.delete(id);
reject(new Error(t('failure')));
}, 15000);
pending.set(id, { resolve: resolve, reject: reject, timeout: timeout });
});
}
function unwrap(value) {
var payload = normalize(value);
if (value && value.isError) throw new Error(payload && payload.error ? text(payload.error) : t('failure'));
if (payload && payload.error) throw new Error(text(payload.error));
return payload || value;
}
function make(tag, value, className) {
var node = document.createElement(tag);
if (className) node.className = className;
if (value !== undefined) node.textContent = value;
return node;
}
function ensurePanel() {
if (elements.panel) return;
var root = document.querySelector('main') || document.body;
var panel = make('section', undefined, 'shd-widget-actions');
panel.hidden = true;
panel.setAttribute('aria-live', 'polite');
var header = make('div', undefined, 'shd-widget-actions-header');
header.append(make('strong', t('actions')), make('span', '', 'shd-widget-actions-selection'));
var select = document.createElement('select');
select.className = 'shd-widget-actions-select';
select.setAttribute('aria-label', t('actions'));
var form = make('form', undefined, 'shd-widget-actions-form');
var status = make('div', '', 'shd-widget-actions-status');
var buttons = make('div', undefined, 'shd-widget-actions-buttons');
var submit = make('button', t('submit'), 'shd-widget-actions-submit');
submit.type = 'submit';
var cancel = make('button', t('cancel'), 'shd-widget-actions-cancel');
cancel.type = 'button';
buttons.append(submit, cancel);
panel.append(header, select, form, status, buttons);
root.append(panel);
elements = { panel: panel, selection: header.lastChild, select: select, form: form, status: status, submit: submit, cancel: cancel };
select.addEventListener('change', function () {
var selected = actionList().find(function (action) { return action.id === select.value; });
state.action = selected || null;
renderForm();
});
cancel.addEventListener('click', function () {
state.action = null;
select.value = '';
renderForm();
});
form.addEventListener('submit', function (event) {
event.preventDefault();
submitAction();
});
}
function inputValue(fieldConfig) {
var value = firstValue(fieldConfig.source || []);
if (value === undefined && fieldConfig.default !== undefined) value = fieldConfig.default;
return value;
}
function createInput(fieldConfig) {
var type = fieldConfig.type || 'text';
var input;
if (type === 'textarea' || type === 'json') {
input = document.createElement('textarea');
input.rows = type === 'json' ? 4 : 3;
if (type === 'json') input.placeholder = '{ }';
} else if (type === 'select') {
input = document.createElement('select');
(fieldConfig.options || []).forEach(function (option) {
var item = document.createElement('option');
item.value = String(option[0]);
item.textContent = state.lang === 'ru' ? String(option[1]) : String(option[2] || option[1]);
input.append(item);
});
} else {
input = document.createElement('input');
input.type = type === 'number' || type === 'email' ? type : 'text';
}
input.className = 'shd-widget-actions-input';
input.name = fieldConfig.name;
var value = inputValue(fieldConfig);
if (type === 'checkbox') {
input.type = 'checkbox';
input.checked = value === true || value === 'true' || value === 1 || value === '1';
} else if (value !== undefined && value !== null) {
input.value = type === 'json' && typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value);
}
if (fieldConfig.required) input.required = true;
return input;
}
function renderForm() {
ensurePanel();
var selectedActionId = state.action ? state.action.id : '';
elements.select.replaceChildren();
var placeholder = document.createElement('option');
placeholder.value = '';
placeholder.textContent = t('chooseAction');
elements.select.append(placeholder);
actionList().forEach(function (action) {
var option = document.createElement('option');
option.value = action.id;
option.textContent = actionLabel(action);
elements.select.append(option);
});
elements.select.value = selectedActionId;
elements.select.hidden = actionList().length === 0;
elements.form.replaceChildren();
elements.status.textContent = '';
elements.submit.disabled = state.busy;
var action = state.action;
if (!action) {
elements.selection.textContent = state.selected ? text(state.selected.title || state.selected.name || state.selected.id || '') : '';
elements.submit.hidden = true;
elements.cancel.hidden = true;
if (actionList().length && !state.selected && actionList().every(hasTargets)) {
elements.form.append(make('div', t('selectRecord'), 'shd-widget-actions-hint'));
}
return;
}
elements.selection.textContent = state.selected ? text(state.selected.title || state.selected.name || state.selected.id || '') : '';
elements.submit.hidden = false;
elements.cancel.hidden = false;
if (hasTargets(action) && !state.selected) elements.form.append(make('div', t('selectRecord'), 'shd-widget-actions-hint'));
(action.fields || []).forEach(function (fieldConfig) {
if (!fieldConfig || !fieldConfig.name || fieldConfig.generated || fieldConfig.type === 'hidden') return;
var wrapper = make('label', undefined, 'shd-widget-actions-field');
wrapper.append(make('span', label(fieldConfig.label)));
wrapper.append(createInput(fieldConfig));
elements.form.append(wrapper);
});
if (action.confirm) {
var confirmLabel = make('label', undefined, 'shd-widget-actions-confirm');
var confirm = document.createElement('input');
confirm.type = 'checkbox';
confirm.name = '__confirm';
confirm.required = true;
confirmLabel.append(confirm, make('span', t('confirm')));
elements.form.append(confirmLabel);
}
var notice = make('div', action.destructive ? t('destructive') : t('warning'), action.destructive ? 'shd-widget-actions-warning shd-widget-actions-danger' : 'shd-widget-actions-warning');
elements.form.append(notice);
}
function coerce(value, type) {
if (type === 'number') {
var number = Number(value);
return Number.isFinite(number) ? number : value;
}
if (type === 'checkbox') return Boolean(value);
if (type === 'json') {
try { return JSON.parse(value); } catch (error) { throw new Error(t('invalidJson')); }
}
return value;
}
function targetType(name) {
return /^(task|document|contract|booking|item|monitor|proposal|approval|page|notification|topic|membership|organization|space|status|project)_?id$/.test(name) || name === 'issue_number' || name === 'number' ? 'number' : 'text';
}
function targetList(action) {
if (Array.isArray(action.targets)) return action.targets;
return action.target ? [action.target] : [];
}
function collectArguments(action) {
var args = Object.assign({}, action.fixed || {});
(action.context || []).forEach(function (name) {
var value = firstValue([name]);
if (value !== undefined && value !== null && value !== '') args[name] = value;
});
targetList(action).forEach(function (targetConfig) {
var value = firstValue(targetConfig.source || targetConfig.name);
if (value === undefined || value === null || value === '') throw new Error(t('selectRecord'));
args[targetConfig.name] = coerce(value, targetConfig.type || targetType(targetConfig.name));
});
(action.fields || []).forEach(function (fieldConfig) {
if (!fieldConfig || !fieldConfig.name || fieldConfig.generated || fieldConfig.type === 'hidden') return;
var input = Array.prototype.find.call(elements.form.querySelectorAll('[name]'), function (node) {
return node.getAttribute('name') === fieldConfig.name;
});
if (!input) return;
var raw = fieldConfig.type === 'checkbox' ? input.checked : input.value.trim();
if (raw === '' && !input.checked && !fieldConfig.required) return;
if (raw === '' && fieldConfig.required) throw new Error(t('required'));
args[fieldConfig.name] = coerce(raw, fieldConfig.type || 'text');
});
if (action.idempotencyKey) args.idempotency_key = 'shd-widget-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
if (action.confirmArg) args.confirm = true;
return args;
}
function setStatus(message, error) {
elements.status.textContent = message || '';
elements.status.className = 'shd-widget-actions-status' + (error ? ' shd-widget-actions-status-error' : '');
}
function submitAction() {
if (state.busy || !state.action) return;
var action = state.action;
var args;
try { args = collectArguments(action); } catch (error) { setStatus(error.message || t('failure'), true); return; }
state.busy = true;
elements.submit.disabled = true;
elements.cancel.disabled = true;
setStatus(t('loading'), false);
request('tools/call', { name: action.tool, arguments: args }).then(function (result) {
unwrap(result);
setStatus(t('success'), false);
var refresh = document.getElementById('refresh');
if (refresh && typeof refresh.click === 'function') window.setTimeout(function () { refresh.click(); }, 250);
}).catch(function (error) {
setStatus(error && error.message ? error.message : t('failure'), true);
}).finally(function () {
state.busy = false;
elements.submit.disabled = false;
elements.cancel.disabled = false;
});
}
function recordFromNode(node) {
var data = node && node.dataset ? node.dataset : {};
var recordId = String(data.shdRecordId || '').toLowerCase();
if (recordId) {
var exact = state.records.find(function (item) {
return text(item.id || item.code || item.key || item.number).toLowerCase() === recordId;
});
if (exact) return exact;
}
var recordTitle = String(data.shdRecordTitle || '').toLowerCase();
if (recordTitle) {
var titled = state.records.find(function (item) {
return text(item.title || item.name || item.display_name || item.number).toLowerCase() === recordTitle;
});
if (titled) return titled;
}
var ariaLabel = node && node.getAttribute ? String(node.getAttribute('aria-label') || '').toLowerCase() : '';
return state.records.find(function (item) {
var title = text(item.title || item.name || item.display_name || item.number).toLowerCase();
return title && title === ariaLabel;
}) || null;
}
function update(value) {
var payload = normalize(value) || {};
var widget = widgetFrom(payload);
if (Object.keys(widget).length) state.widget = widget;
var records = recordsFrom(payload);
if (records.length || Array.isArray(payload.data) || Array.isArray(payload.items) || Array.isArray(payload.projects)) {
state.records = records;
if (state.selected && !state.records.some(function (item) {
var selectedId = text(state.selected.id || state.selected.code || state.selected.key || state.selected.number).toLowerCase();
var itemId = text(item.id || item.code || item.key || item.number).toLowerCase();
return selectedId && itemId && selectedId === itemId;
})) {
state.selected = null;
state.action = null;
}
}
ensurePanel();
var actions = actionList();
elements.panel.hidden = actions.length === 0;
if (actions.length && !state.action) renderForm();
else if (actions.length) renderForm();
}
document.addEventListener('click', function (event) {
var node = event.target && event.target.closest ? event.target.closest('.record,.project-row,.visual-card,.progress-card,.preview-card,.calendar-card,.timeline-card,.kanban-card') : null;
if (!node || (elements.panel && elements.panel.contains(node))) return;
var item = recordFromNode(node);
if (!item) return;
state.selected = item;
ensurePanel();
renderForm();
}, true);
window.addEventListener('shd-widget-data', function (event) {
update(event && event.detail);
});
window.__SHD_WIDGET_ACTIONS_UPDATE__ = update;
window.addEventListener('message', function (event) {
if (event.source !== window.parent) return;
var message = event.data;
if (!message || message.jsonrpc !== '2.0') return;
if (message.id !== undefined && pending.has(message.id)) {
var requestState = pending.get(message.id);
pending.delete(message.id);
window.clearTimeout(requestState.timeout);
if (message.error) requestState.reject(new Error(message.error.message || t('failure')));
else requestState.resolve(message.result);
return;
}
}, { passive: true });
ensurePanel();
var initial = typeof window !== 'undefined' && (window.__SHD_WIDGET_STATE__ || (window.openai && (window.openai.toolOutput || window.openai.toolInput)));
if (initial) update(initial);
}());
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-acl-matrix",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/acl-matrix/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-active-projects",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/active-projects/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-activity",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/activity/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-agents-health",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/agents-health/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-agents",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/agents/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-airbase-preview",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/airbase-preview/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-airbase",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/airbase/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-controller-observability",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/controller-observability/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-crm-funnel",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/crm-funnel/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-crm",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"uri": "ui://shd/crm/v1.html",
"mediaType": "text/html;profile=mcp-app"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-discussions",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/discussions/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-documents-completeness",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/documents-completeness/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-documents",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"uri": "ui://shd/documents/v1.html",
"mediaType": "text/html;profile=mcp-app"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-files",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/files/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-finance-dashboard",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/finance-dashboard/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-finance",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"uri": "ui://shd/finance/v1.html",
"mediaType": "text/html;profile=mcp-app"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-gitea-board",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/gitea-board/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-gitea",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/gitea/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-inventory-warnings",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/inventory-warnings/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-inventory",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/inventory/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-notes-tree",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/notes-tree/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-notes",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/notes/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-notifications",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/notifications/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-organizations",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/organizations/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-pdf-scheme-editor-progress",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/pdf-scheme-editor-progress/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-pdf-scheme-editor",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/pdf-scheme-editor/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-project-overview",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/project-overview/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-project-timeline",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/project-timeline/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-proposal-approvals",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/proposal-approvals/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-proposals",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/proposals/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-schedule-calendar",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/schedule-calendar/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-scheduling",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/scheduling/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-status-page",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/status-page/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-task-kanban",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/task-kanban/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-tasks",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"uri": "ui://shd/tasks/v1.html",
"mediaType": "text/html;profile=mcp-app"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"name": "shd-team-workload",
"version": "1.0.0",
"resources": [
{
"file": "index.html",
"mediaType": "text/html;profile=mcp-app",
"uri": "ui://shd/team-workload/v1.html"
}
],
"license": "../../../../../LICENSE",
"source": {
"repository": "https://github.com/bulava92/shd-mcp-plugin"
}
}