Unify MCP widgets and complete localization
This commit is contained in:
@@ -46,6 +46,13 @@ The expected flow is:
|
|||||||
3. The selected widget renders a register, can call the matching detail tool for
|
3. The selected widget renders a register, can call the matching detail tool for
|
||||||
one selected row and exposes controlled write actions when declared by the
|
one selected row and exposes controlled write actions when declared by the
|
||||||
render metadata.
|
render metadata.
|
||||||
|
4. For controller requests, use the explicit controller data→render pair:
|
||||||
|
`shd_get_controller_source_inventory` → `shd_render_controller_devices_widget`,
|
||||||
|
`shd_get_home_live_state` → `shd_render_controller_live_state_widget`,
|
||||||
|
`shd_get_controller_source_history` → `shd_render_controller_history_widget`,
|
||||||
|
or `shd_get_controller_state_statistics` →
|
||||||
|
`shd_render_controller_statistics_widget`. Pass the complete data result and
|
||||||
|
preserve `project`, `controller` and `item_id` in `meta.sourceArgs`.
|
||||||
|
|
||||||
The widget resources are versioned at `ui://shd/active-projects/v1.html`,
|
The widget resources are versioned at `ui://shd/active-projects/v1.html`,
|
||||||
`ui://shd/documents/v1.html`, `ui://shd/tasks/v1.html`,
|
`ui://shd/documents/v1.html`, `ui://shd/tasks/v1.html`,
|
||||||
@@ -53,6 +60,7 @@ The widget resources are versioned at `ui://shd/active-projects/v1.html`,
|
|||||||
`ui://shd/discussions/v1.html`, `ui://shd/notifications/v1.html`,
|
`ui://shd/discussions/v1.html`, `ui://shd/notifications/v1.html`,
|
||||||
`ui://shd/scheduling/v1.html`, `ui://shd/inventory/v1.html`,
|
`ui://shd/scheduling/v1.html`, `ui://shd/inventory/v1.html`,
|
||||||
`ui://shd/agents/v1.html`, `ui://shd/status-page/v1.html`,
|
`ui://shd/agents/v1.html`, `ui://shd/status-page/v1.html`,
|
||||||
|
`ui://shd/controller-observability/v1.html`,
|
||||||
`ui://shd/project-db/v1.html`, `ui://shd/notes/v1.html`,
|
`ui://shd/project-db/v1.html`, `ui://shd/notes/v1.html`,
|
||||||
`ui://shd/project-overview/v1.html`, `ui://shd/files/v1.html`,
|
`ui://shd/project-overview/v1.html`, `ui://shd/files/v1.html`,
|
||||||
`ui://shd/proposals/v1.html`, `ui://shd/terms/v1.html`,
|
`ui://shd/proposals/v1.html`, `ui://shd/terms/v1.html`,
|
||||||
|
|||||||
@@ -61,11 +61,15 @@ Finance, CRM, Discussions, Notifications, Scheduling, Inventory, Agents,
|
|||||||
Status Page, Project DB, Notes, Project overview, Files, Proposals, Terms,
|
Status Page, Project DB, Notes, Project overview, Files, Proposals, Terms,
|
||||||
Activity, Organizations and access, Gitea Issues and Schemes:
|
Activity, Organizations and access, Gitea Issues and Schemes:
|
||||||
|
|
||||||
Controller observability uses one parameterized resource for source history,
|
Controller observability uses one parameterized resource for device inventory,
|
||||||
cross-controller comparisons, expert settings, complete project topology,
|
current state, server-computed statistics, source history, cross-controller
|
||||||
telemetry health and source audit:
|
comparisons, expert settings, complete project topology, telemetry health and
|
||||||
|
source audit:
|
||||||
|
|
||||||
- `shd_render_controller_history_widget` → `ui://shd/controller-observability/v1.html`;
|
- `shd_render_controller_history_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_statistics_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_devices_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
- `shd_render_controller_live_state_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
- `shd_render_controller_compare_widget` → `ui://shd/controller-observability/v1.html`;
|
- `shd_render_controller_compare_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
- `shd_render_controller_settings_widget` → `ui://shd/controller-observability/v1.html`;
|
- `shd_render_controller_settings_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
- `shd_render_controller_topology_widget` → `ui://shd/controller-observability/v1.html`;
|
- `shd_render_controller_topology_widget` → `ui://shd/controller-observability/v1.html`;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ modules. Skills do not grant permissions or bypass server-side ACLs.
|
|||||||
- linked Gitea issues, labels, comments and attachments;
|
- linked Gitea issues, labels, comments and attachments;
|
||||||
- safe bulk-change planning and validation rules.
|
- safe bulk-change planning and validation rules.
|
||||||
|
|
||||||
When the connected MCP host supports MCP Apps UI, the 42 SHD render tools have
|
When the connected MCP host supports MCP Apps UI, the 44 SHD render tools have
|
||||||
inline interactive widgets: active projects, module registers and specialized
|
inline interactive widgets: active projects, module registers and specialized
|
||||||
Kanban, workload, timeline, funnel, dashboard, calendar, matrix, tree and
|
Kanban, workload, timeline, funnel, dashboard, calendar, matrix, tree and
|
||||||
Project DB views. Each widget sorts or filters normalized records, shows
|
Project DB views. Each widget sorts or filters normalized records, shows
|
||||||
@@ -46,19 +46,31 @@ The same tools still return normal structured data for hosts that do not render
|
|||||||
widgets.
|
widgets.
|
||||||
|
|
||||||
The controller observability render tools share the versioned
|
The controller observability render tools share the versioned
|
||||||
`widgets/controller-observability/v1/` resource. It exposes explicit native vs
|
`widgets/controller-observability/v1/` resource. It exposes controller device
|
||||||
Influx/Grafana provenance, universal comparison presets, complete controller
|
inventory and profiles, normalized live states, server-computed binary/numeric
|
||||||
topology, telemetry health and operation audit. Expert-setting controls are enabled only
|
statistics, explicit native vs Influx/Grafana provenance, universal comparison
|
||||||
for source-provided writable descriptors.
|
presets, complete controller topology, telemetry health and operation audit.
|
||||||
|
Expert-setting controls are enabled only for source-provided writable descriptors.
|
||||||
|
|
||||||
The active-projects widget is versioned in `widgets/active-projects/v1/` with a
|
The active-projects widget is versioned in `widgets/active-projects/v1/` with a
|
||||||
manifest, SHA256 checksum and source/provenance note. The SHD MCP backend reads
|
manifest, SHA256 checksum and source/provenance note. The SHD MCP backend reads
|
||||||
that artifact for the `ui://shd/active-projects/v1.html` resource.
|
that artifact for the `ui://shd/active-projects/v1.html` resource.
|
||||||
|
|
||||||
The other versioned resources are stored under `widgets/` with a manifest,
|
The standard module resources are generated from the single
|
||||||
SHA256 checksum and source/provenance note for each resource. Their render
|
`widgets/_shared/universal-widget.html` source by
|
||||||
tools are deliberately separate from the list tools so the server can keep
|
`scripts/generate-widget-artifacts.mjs`. Each generated artifact still keeps
|
||||||
data access and UI rendering auditable.
|
its own URI, manifest, SHA256 checksum and provenance note: this preserves
|
||||||
|
resource cache keys and module-scope registration while removing duplicated
|
||||||
|
HTML, bridge and action runtime code. The generator supports `--check` for a
|
||||||
|
no-write consistency check. Module copy and fallback tool configuration lives
|
||||||
|
in the sibling `widgets/_shared/universal-widget-config.json` source and is
|
||||||
|
injected into each standalone artifact by the generator. For an older template
|
||||||
|
that still contains the inline configuration block, run `--extract-config`
|
||||||
|
once and then `--migrate-template` once. The
|
||||||
|
active-projects and controller-observability resources remain separate because
|
||||||
|
they are specialized applications with different interaction contracts. Render
|
||||||
|
tools are deliberately separate from list tools so data access and UI rendering
|
||||||
|
remain auditable.
|
||||||
|
|
||||||
## MCP connection
|
## MCP connection
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,23 @@ configuration.
|
|||||||
- Keep diagnostics, configuration, endpoint publication and network routing as
|
- Keep diagnostics, configuration, endpoint publication and network routing as
|
||||||
separate operations. Never expose credentials, private keys or tunnel data.
|
separate operations. Never expose credentials, private keys or tunnel data.
|
||||||
|
|
||||||
|
## Controller data and widgets
|
||||||
|
|
||||||
|
- For a controller device list, call `shd_get_controller_source_inventory`, then
|
||||||
|
`shd_render_controller_devices_widget` with the complete result and scoped
|
||||||
|
`meta.sourceArgs`.
|
||||||
|
- For current states across controllers, call `shd_get_home_live_state`, then
|
||||||
|
`shd_render_controller_live_state_widget`.
|
||||||
|
- For one presence, binary state or numeric sensor statistics, call
|
||||||
|
`shd_get_controller_state_statistics`, then
|
||||||
|
`shd_render_controller_statistics_widget`. The server result is authoritative
|
||||||
|
for counts, intervals, durations, percentages, medians and hourly buckets;
|
||||||
|
do not calculate them from raw history in the model.
|
||||||
|
- For raw time-series history, call `shd_get_controller_source_history`, then
|
||||||
|
`shd_render_controller_history_widget`. Keep the detailed rows in the widget
|
||||||
|
after a successful render and use plain text only when the UI call is
|
||||||
|
unavailable or fails.
|
||||||
|
|
||||||
## High-impact actions
|
## High-impact actions
|
||||||
|
|
||||||
Creating/closing tunnels, publishing or revoking endpoints, replacing access
|
Creating/closing tunnels, publishing or revoking endpoints, replacing access
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ readback. Do not invent fields, statuses, identifiers, routes or capabilities.
|
|||||||
- Notifications, preferences or read state: use Notifications tools and the `shd-notifications` workflow.
|
- Notifications, preferences or read state: use Notifications tools and the `shd-notifications` workflow.
|
||||||
- Inventory, stock, assets, procurement or stocktakes: use Inventory tools and the `shd-inventory` workflow.
|
- Inventory, stock, assets, procurement or stocktakes: use Inventory tools and the `shd-inventory` workflow.
|
||||||
- Agents, controllers, diagnostics, tunnels or network access: use Agents tools and the `shd-agents-operations` workflow.
|
- Agents, controllers, diagnostics, tunnels or network access: use Agents tools and the `shd-agents-operations` workflow.
|
||||||
|
- Controller devices, current states, presence/history or sensor statistics: use the controller-integrations source tools; in a UI-capable host call the matching controller render tool after the data tool and before the final response (`shd_render_controller_devices_widget`, `shd_render_controller_live_state_widget`, `shd_render_controller_history_widget` or `shd_render_controller_statistics_widget`).
|
||||||
- Status-page monitors, history, groups or incidents: use Status Page tools and the `shd-status-page` workflow.
|
- Status-page monitors, history, groups or incidents: use Status Page tools and the `shd-status-page` workflow.
|
||||||
- Realtime sessions, event cursors or historical activity: use Realtime tools and the `shd-realtime-activity` workflow.
|
- Realtime sessions, event cursors or historical activity: use Realtime tools and the `shd-realtime-activity` workflow.
|
||||||
- Terms documents, revisions, completeness or contract discussions: use Terms tools and the `shd-terms-contracts` workflow.
|
- Terms documents, revisions, completeness or contract discussions: use Terms tools and the `shd-terms-contracts` workflow.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,558 @@
|
|||||||
|
<!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>
|
||||||
|
:root{color-scheme:light dark;--bg:#f8fafc;--surface:#e8edf2;--hover:#e1e8ef;--line:rgba(31,41,55,.16);--ink:#1d1f25;--muted:#616670;--accent:#535963;--soft:rgba(75,85,99,.12);--bad:#b42318;--good:#2f855a;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:980px;margin:auto;padding:22px}.card{padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--surface);box-shadow:var(--shadow)}
|
||||||
|
.eyebrow{color:var(--muted);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 5px;font-size:25px;line-height:1.15}.lede{margin:0;color:var(--muted)}.summary{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 14px}.summary span{padding:5px 9px;border:1px solid var(--line);border-radius:999px;background:var(--hover);color:var(--muted)}.summary strong{color:var(--ink)}
|
||||||
|
.toolbar{display:flex;gap:8px;margin-bottom:10px}.search{min-width:0;flex:1;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);color:inherit;outline:0}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--soft)}button{cursor:pointer}.refresh{padding:10px 14px;border:1px solid var(--accent);border-radius:10px;background:var(--accent);color:var(--button);font-weight:650}.refresh:disabled{opacity:.6;cursor:wait}.status{min-height:22px;color:var(--muted)}.error{color:var(--bad)}
|
||||||
|
.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:560px}th,td{padding:11px 13px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--hover);color:var(--muted);font-size:12px;font-weight:650;z-index:1}tbody tr:last-child td{border-bottom:0}.record{cursor:pointer;outline:0}.record:hover,.record:focus{background:var(--soft)}.title{font-weight:650}.sub{display:block;color:var(--muted);font-size:12px;margin-top:2px}.pill{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px;white-space:nowrap}.pill.up,.pill.ok,.pill.active{color:var(--good);background:rgba(47,133,90,.14)}.pill.down,.pill.error,.pill.overdue{color:var(--bad);background:rgba(180,35,24,.12)}.empty{padding:24px;text-align:center;color:var(--muted)}
|
||||||
|
.visual[hidden]{display:none}.visual{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:12px;background:var(--bg);padding:12px}.visual-card{display:block;width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:var(--surface);color:inherit;text-align:left;cursor:pointer}.visual-card:hover,.visual-card:focus{background:var(--hover);border-color:var(--accent);outline:0}.visual-title{display:block;font-weight:650}.visual-date,.visual-value{display:block;color:var(--muted);font-size:12px;margin-top:4px}.visual-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(190px,1fr);gap:10px;overflow-x:auto;align-items:start}.kanban-col{min-width:190px;padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface)}.kanban-head{display:flex;justify-content:space-between;gap:8px;margin-bottom:8px;font-weight:650}.kanban-list{display:grid;gap:8px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:10px;margin-bottom:12px}.metric{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.metric-label{display:block;color:var(--muted);font-size:12px}.metric-value{display:block;font-size:22px;font-weight:700;margin-top:4px}.timeline{display:grid;gap:0}.timeline-item{display:grid;grid-template-columns:18px minmax(0,1fr);gap:10px;position:relative;padding-bottom:10px}.timeline-rail{position:absolute;left:8px;top:16px;bottom:0;width:2px;background:var(--line)}.timeline-item:last-child .timeline-rail{display:none}.timeline-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);border:3px solid var(--soft);z-index:1}.timeline-content{min-width:0}.timeline-meta{color:var(--muted);font-size:12px;margin-bottom:5px}.funnel{display:grid;gap:9px}.funnel-row{display:grid;grid-template-columns:minmax(100px,1fr) minmax(120px,2fr) auto;gap:10px;align-items:center}.funnel-fill{width:100%;height:10px;accent-color:var(--accent)}.progress-card{display:grid;gap:7px;padding:11px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.progress{width:100%;height:10px;accent-color:var(--accent)}.calendar{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px}.calendar-day{padding:10px;border:1px solid var(--line);border-radius:12px;background:var(--surface);display:grid;gap:8px;align-content:start}.calendar-head{font-weight:650}.matrix-wrap{overflow:auto}.matrix-table{width:100%;border-collapse:collapse;min-width:560px}.matrix-table th,.matrix-table td{padding:9px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}.matrix-table th{color:var(--muted);font-size:12px;background:var(--hover)}.tree{display:grid;gap:7px}.tree-children{display:grid;gap:7px;padding-left:18px;border-left:1px solid var(--line)}.preview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px}.preview-card{padding:12px;border:1px solid var(--line);border-radius:10px;background:var(--surface)}.preview-fields{display:grid;gap:5px;margin-top:8px}.preview-field{font-size:12px;overflow-wrap:anywhere}.preview-field strong{color:var(--muted);font-weight:500}@media(max-width:640px){.funnel-row{grid-template-columns:1fr auto}.funnel-fill{grid-column:1/-1}}
|
||||||
|
.details{margin-top:14px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.details[hidden]{display:none}.detail-head{display:flex;justify-content:space-between;gap:12px;align-items:start}.details h2{margin:0;font-size:18px}.close{border:0;background:transparent;color:var(--muted);font-size:20px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:14px}.detail{padding:9px;border-radius:8px;background:var(--surface)}.label{display:block;color:var(--muted);font-size:11px;margin-bottom:3px}.value{overflow-wrap:anywhere;white-space:pre-wrap}@media(max-width:640px){.shell{padding:12px}.card{padding:15px}.toolbar{display:grid;grid-template-columns:minmax(0,1fr) auto}}
|
||||||
|
@media(prefers-color-scheme:dark){:root{--bg:#202020;--surface:#292929;--hover:#303336;--line:#43474c;--ink:#eaecf0;--muted:#96999d;--accent:#c7b07a;--soft:rgba(199,176,122,.18);--bad:#ff9a93;--good:#83c995;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
|
</style>
|
||||||
|
<style data-shd-widget-write-actions="css">
|
||||||
|
.shd-widget-actions{margin-top:14px;padding:14px;border:1px solid var(--border,var(--line,rgba(31,41,55,.16)));border-radius:11px;background:var(--surface,var(--panel,#e8edf2));color:var(--text,var(--ink,#1d1f25))}
|
||||||
|
.shd-widget-actions[hidden]{display:none}
|
||||||
|
.shd-widget-actions-header{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px}
|
||||||
|
.shd-widget-actions-selection{overflow:hidden;color:var(--muted,#616670);font-size:12px;text-overflow:ellipsis;white-space:nowrap}
|
||||||
|
.shd-widget-actions-select,.shd-widget-actions-input{width:100%;border:1px solid var(--border,var(--line,rgba(31,41,55,.16)));border-radius:8px;padding:8px 10px;background:var(--bg,#f8fafc);color:var(--text,var(--ink,#1d1f25))}
|
||||||
|
.shd-widget-actions-select:focus,.shd-widget-actions-input:focus{border-color:var(--accent,#535963);outline:0;box-shadow:0 0 0 3px var(--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:10px;margin-top:10px}
|
||||||
|
.shd-widget-actions-field{display:grid;gap:4px;color: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(--accent,#535963)}
|
||||||
|
.shd-widget-actions-confirm{display:flex;grid-column:1/-1;align-items:center;gap:7px;color:var(--text,var(--ink,#1d1f25));font-size:12px}
|
||||||
|
.shd-widget-actions-warning,.shd-widget-actions-hint{grid-column:1/-1;color:var(--muted,#616670);font-size:12px}
|
||||||
|
.shd-widget-actions-danger{color:var(--danger,var(--bad,#b42318))}
|
||||||
|
.shd-widget-actions-status{min-height:18px;margin-top:9px;color:var(--muted,#616670);font-size:12px}
|
||||||
|
.shd-widget-actions-status-error{color:var(--danger,var(--bad,#b42318))}
|
||||||
|
.shd-widget-actions-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}
|
||||||
|
.shd-widget-actions-buttons button{border:1px solid var(--border,var(--line,rgba(31,41,55,.16)));border-radius:8px;padding:8px 11px;background:var(--bg,#f8fafc);color:var(--text,var(--ink,#1d1f25));cursor:pointer}
|
||||||
|
.shd-widget-actions-buttons button:hover{background:var(--surface-hover,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:3px}}
|
||||||
|
</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',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=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 dateText(x){if(!x)return'—';var n=Date.parse(x);if(Number.isNaN(n))return String(x);try{return new Intl.DateTimeFormat(state.lang,{dateStyle:'medium',timeStyle:'short'}).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 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;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 visualCard(x,extra){var b=make('button',undefined,'visual-card'+(extra?' '+extra:''));b.type='button';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=Math.max.apply(null,Object.values(groups));var 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=make('article',undefined,'progress-card');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');day.append(make('div',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=make('tr');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)};row.className='record';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=make('article',undefined,'preview-card');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';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 render(x){var p=unwrap(x);applyConfig(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});renderRows()}
|
||||||
|
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;e.grid.replaceChildren();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:'2025-11-25',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">
|
||||||
|
(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 = 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 content = String(node.textContent || '').toLowerCase();
|
||||||
|
var exact = state.records.find(function (item) {
|
||||||
|
var id = text(item.id || item.code || item.key || item.number).toLowerCase();
|
||||||
|
return id && content.indexOf(id) !== -1;
|
||||||
|
});
|
||||||
|
if (exact) return exact;
|
||||||
|
return state.records.find(function (item) {
|
||||||
|
var title = text(item.title || item.name || item.display_name || item.number).toLowerCase();
|
||||||
|
return title && content.indexOf(title) !== -1;
|
||||||
|
}) || 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;
|
||||||
|
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('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;
|
||||||
|
}
|
||||||
|
if (message.method === 'ui/notifications/tool-input' || message.method === 'ui/notifications/tool-result') update(message.params || {});
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
ensurePanel();
|
||||||
|
var initial = typeof window !== 'undefined' && window.openai ? (window.openai.toolOutput || window.openai.toolInput) : null;
|
||||||
|
if (initial) update(initial);
|
||||||
|
}());
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1 +1 @@
|
|||||||
9e82d5a6f3c35445b5d7400fd4b1aa8ba93c97141cf83a23e585ef051e3f0421 index.html
|
e0595452b8365206b4bb26cf5b2680c8da2e7107260a0caf7bb0c5c23008743b index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/acl-matrix/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/acl-matrix/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only organization membership and permission matrix.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77269,
|
"bytes": 78910,
|
||||||
"gzipBytes": 20605,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/acl-matrix/v1.html",
|
"uri": "ui://shd/acl-matrix/v1.html",
|
||||||
"sha256": "9e82d5a6f3c35445b5d7400fd4b1aa8ba93c97141cf83a23e585ef051e3f0421"
|
"sha256": "e0595452b8365206b4bb26cf5b2680c8da2e7107260a0caf7bb0c5c23008743b"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/acl-matrix/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
463ba23dc7332ba8a2e5340bb19d3951d798c7ecc700209fc46f659cfdbe253c index.html
|
762673741389a380d738687823f4badf206d4c248ea3790f0d94f7921931c6d1 index.html
|
||||||
|
|||||||
@@ -265,7 +265,7 @@
|
|||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var timeout = window.setTimeout(function () {
|
var timeout = window.setTimeout(function () {
|
||||||
pendingRequests.delete(id);
|
pendingRequests.delete(id);
|
||||||
reject(new Error('Host request timed out'));
|
reject(new Error(t.refreshError));
|
||||||
}, 10000);
|
}, 10000);
|
||||||
pendingRequests.set(id, { resolve: resolve, reject: reject, timeout: timeout });
|
pendingRequests.set(id, { resolve: resolve, reject: reject, timeout: timeout });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 45447,
|
"bytes": 45437,
|
||||||
"gzipBytes": 11470,
|
"gzipBytes": 11305,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/active-projects/v1.html",
|
"uri": "ui://shd/active-projects/v1.html",
|
||||||
"sha256": "463ba23dc7332ba8a2e5340bb19d3951d798c7ecc700209fc46f659cfdbe253c"
|
"sha256": "762673741389a380d738687823f4badf206d4c248ea3790f0d94f7921931c6d1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
fb24651b2efe2b66c490b839d9c7c3c679c77c1f58f00d9bcfce199c522761ca index.html
|
870534faae707f202e8fb1d21fdbeee0fc1432236698ae277b5dd99b1f331b51 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/activity/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/activity/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Authenticated realtime activity stream.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 53670,
|
"bytes": 78906,
|
||||||
"gzipBytes": 15487,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/activity/v1.html",
|
"uri": "ui://shd/activity/v1.html",
|
||||||
"sha256": "fb24651b2efe2b66c490b839d9c7c3c679c77c1f58f00d9bcfce199c522761ca"
|
"sha256": "870534faae707f202e8fb1d21fdbeee0fc1432236698ae277b5dd99b1f331b51"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/activity/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
866750c8b84f0879d50035ce6fb4b58174a33c3dafdfa7ed72a9ef747c6d4146 index.html
|
dac1b47d9e6008708b464f7e310125ed1709e07955bd0c6adc5e9595c6bb5a09 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/agents-health/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/agents-health/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only agent health overview; controller topology and telemetry health are rendered by the controller-observability widget.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77403,
|
"bytes": 78916,
|
||||||
"gzipBytes": 20585,
|
"gzipBytes": 21188,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/agents-health/v1.html",
|
"uri": "ui://shd/agents-health/v1.html",
|
||||||
"sha256": "866750c8b84f0879d50035ce6fb4b58174a33c3dafdfa7ed72a9ef747c6d4146"
|
"sha256": "dac1b47d9e6008708b464f7e310125ed1709e07955bd0c6adc5e9595c6bb5a09"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/agents-health/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
b556fb2c97029bf075b97613dbd1e90cda5576c0f13be463c2becd3c0db2a0e6 index.html
|
260762f0d2f01562d087ea0d6cf7a84c374ee047fd56527fd89612472d065ce2 index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Agents widget
|
# Agents widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/agents/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/agents/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides agent entities with read-only status context; project controller topology is rendered by the separate controller-observability widget.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 49008,
|
"bytes": 78902,
|
||||||
"gzipBytes": 14343,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/agents/v1.html",
|
"uri": "ui://shd/agents/v1.html",
|
||||||
"sha256": "b556fb2c97029bf075b97613dbd1e90cda5576c0f13be463c2becd3c0db2a0e6"
|
"sha256": "260762f0d2f01562d087ea0d6cf7a84c374ee047fd56527fd89612472d065ce2"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/agents/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
c2fcb36329b0588f621577863f6b7549bf622732fd88d4bcead254bd68b6642c index.html
|
fca8fbc1630bcb60d699442903900cec2eee02e863bb5d8523c6317b4d2d1b53 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/airbase-preview/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/airbase-preview/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only AirBase table record preview.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 76306,
|
"bytes": 78920,
|
||||||
"gzipBytes": 20462,
|
"gzipBytes": 21188,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/airbase-preview/v1.html",
|
"uri": "ui://shd/airbase-preview/v1.html",
|
||||||
"sha256": "c2fcb36329b0588f621577863f6b7549bf622732fd88d4bcead254bd68b6642c"
|
"sha256": "fca8fbc1630bcb60d699442903900cec2eee02e863bb5d8523c6317b4d2d1b53"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/airbase-preview/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4f331c4d7e295f080dfdbcd9360f860696817e0ccd2ffc0ca92f2790330faefb index.html
|
ceb3f0a4bca8c7dfa85ef5fbe705f0767523be2b10637bc1e3f767edab7ccb49 index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# AirBase widget
|
# AirBase widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/airbase/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/airbase/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides table catalog and read-only schema details.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 48201,
|
"bytes": 78904,
|
||||||
"gzipBytes": 14230,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/airbase/v1.html",
|
"uri": "ui://shd/airbase/v1.html",
|
||||||
"sha256": "4f331c4d7e295f080dfdbcd9360f860696817e0ccd2ffc0ca92f2790330faefb"
|
"sha256": "ceb3f0a4bca8c7dfa85ef5fbe705f0767523be2b10637bc1e3f767edab7ccb49"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/airbase/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
de6f1c6e07d74bc58555f41a22e9a0e8a3942577ffce5773f9450707cb7ca440 index.html
|
74cc8cd974ddfb0563b213166398cf37e98cd477e010c9fbcc444324b5366a5c index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/controller-observability/v1.html`.
|
This artifact is served by the SHD MCP backend as `ui://shd/controller-observability/v1.html`.
|
||||||
|
|
||||||
It is a standalone parameterized MCP App. The same UI contract renders controller history, series comparison, expert settings, lightweight topology, telemetry health and source audit modes; source inventory is loaded separately through the bounded paginated inventory request.
|
It is a standalone parameterized MCP App. The same UI contract renders controller device inventory, live state, server-computed state statistics, history, series comparison, expert settings, lightweight topology, telemetry health and source audit modes. A selected device profile is loaded on demand through the profile tool.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>SHD controller observability</title>
|
<title>SHD controller observability</title>
|
||||||
<style>
|
<style>
|
||||||
:root{color-scheme:light dark;--bg:#f7f9fb;--panel:#edf1f5;--line:rgba(31,41,55,.16);--ink:#20242a;--muted:#69717c;--accent:#4f5967;--soft:rgba(79,89,103,.12);--good:#2f855a;--bad:#b42318;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
:root{color-scheme:light dark;--bg:#f7f9fb;--panel:#edf1f5;--line:rgba(31,41,55,.16);--ink:#20242a;--muted:#69717c;--accent:#4f5967;--soft:rgba(79,89,103,.12);--good:#2f855a;--bad:#b42318;--button:#fff;--shadow:0 10px 26px rgba(31,41,55,.1)}
|
||||||
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:1100px;margin:auto;padding:18px}.card{padding:18px;border:1px solid var(--line);border-radius:17px;background:var(--panel);box-shadow:var(--shadow)}.eyebrow{color:var(--muted);font-size:11px;font-weight:750;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 4px;font-size:24px;line-height:1.15}.lede{margin:0;color:var(--muted)}.toolbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:16px 0 10px}.toolbar button,.toolbar select{border:1px solid var(--line);border-radius:9px;padding:9px 11px;background:var(--bg);color:inherit}.toolbar button{cursor:pointer;font-weight:650}.toolbar .primary{border-color:var(--accent);background:var(--accent);color:var(--button)}.toolbar button:disabled{opacity:.58;cursor:wait}.toolbar select{min-width:150px}.status{min-height:22px;margin:4px 0;color:var(--muted)}.error{color:var(--bad)}.summary{display:flex;gap:7px;flex-wrap:wrap;margin:12px 0}.pill{display:inline-block;padding:4px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px}.pill.good{color:var(--good);background:rgba(47,133,90,.14)}.pill.bad{color:var(--bad);background:rgba(180,35,24,.12)}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px}.panel{padding:13px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.panel h2,.panel h3{margin:0 0 8px;font-size:16px}.panel p{margin:4px 0;color:var(--muted)}.chart{min-height:128px;margin:8px 0;padding:7px;border:1px solid var(--line);border-radius:10px;background:var(--bg);overflow:hidden}.chart svg{display:block;width:100%;height:115px}.chart-empty{display:grid;place-items:center;height:112px;color:var(--muted);font-size:12px}.axis{fill:var(--muted);font-size:10px}.line-a{fill:none;stroke:#4f7cac;stroke-width:2.2}.line-b{fill:none;stroke:#b56a3c;stroke-width:2.2}.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:11px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:600px}th,td{padding:9px 11px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--panel);color:var(--muted);font-size:12px;z-index:1}tr:last-child td{border-bottom:0}.muted{color:var(--muted)}.empty{padding:24px;text-align:center;color:var(--muted)}.source-list{display:grid;gap:7px}.source-row{display:flex;justify-content:space-between;gap:10px;align-items:center;padding:8px 10px;border-radius:9px;background:var(--soft)}.setting{display:grid;grid-template-columns:minmax(130px,1fr) minmax(150px,1fr);gap:9px;align-items:center;padding:9px 0;border-bottom:1px solid var(--line)}.setting:last-child{border-bottom:0}.setting label{font-weight:650}.setting small{display:block;color:var(--muted);font-weight:400}.setting input,.setting select{width:100%;border:1px solid var(--line);border-radius:8px;padding:8px;background:var(--bg);color:inherit}.setting input:disabled,.setting select:disabled{opacity:.6}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.actions button{border:1px solid var(--accent);border-radius:8px;padding:8px 12px;background:var(--accent);color:var(--button);cursor:pointer}.actions button:disabled{opacity:.6;cursor:wait}@media(max-width:650px){.shell{padding:10px}.card{padding:14px}.setting{grid-template-columns:1fr}.table-wrap{margin:0 -2px}}
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.shell{max-width:1100px;margin:auto;padding:18px}.card{padding:18px;border:1px solid var(--line);border-radius:17px;background:var(--panel);box-shadow:var(--shadow)}.eyebrow{color:var(--muted);font-size:11px;font-weight:750;letter-spacing:.12em;text-transform:uppercase}h1{margin:4px 0 4px;font-size:24px;line-height:1.15}.lede{margin:0;color:var(--muted)}.toolbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:16px 0 10px}.toolbar button,.toolbar select{border:1px solid var(--line);border-radius:9px;padding:9px 11px;background:var(--bg);color:inherit}.toolbar button{cursor:pointer;font-weight:650}.toolbar .primary{border-color:var(--accent);background:var(--accent);color:var(--button)}.toolbar button:disabled{opacity:.58;cursor:wait}.toolbar select{min-width:150px}.status{min-height:22px;margin:4px 0;color:var(--muted)}.error{color:var(--bad)}.summary{display:flex;gap:7px;flex-wrap:wrap;margin:12px 0}.pill{display:inline-block;padding:4px 8px;border-radius:999px;background:var(--soft);color:var(--accent);font-size:12px}.pill.good{color:var(--good);background:rgba(47,133,90,.14)}.pill.bad{color:var(--bad);background:rgba(180,35,24,.12)}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:10px}.panel{padding:13px;border:1px solid var(--line);border-radius:12px;background:var(--bg)}.panel h2,.panel h3{margin:0 0 8px;font-size:16px}.panel p{margin:4px 0;color:var(--muted)}.chart{min-height:128px;margin:8px 0;padding:7px;border:1px solid var(--line);border-radius:10px;background:var(--bg);overflow:hidden}.chart svg{display:block;width:100%;height:115px}.chart-empty{display:grid;place-items:center;height:112px;color:var(--muted);font-size:12px}.axis{fill:var(--muted);font-size:10px}.line-a{fill:none;stroke:#4f7cac;stroke-width:2.2}.line-b{fill:none;stroke:#b56a3c;stroke-width:2.2}.table-wrap{max-height:460px;overflow:auto;border:1px solid var(--line);border-radius:11px;background:var(--bg)}table{width:100%;border-collapse:collapse;min-width:600px}th,td{padding:9px 11px;border-bottom:1px solid var(--line);text-align:left;vertical-align:top}th{position:sticky;top:0;background:var(--panel);color:var(--muted);font-size:12px;z-index:1}tr:last-child td{border-bottom:0}.muted{color:var(--muted)}.empty{padding:24px;text-align:center;color:var(--muted)}.source-list{display:grid;gap:7px}.source-row{display:flex;justify-content:space-between;gap:10px;align-items:center;padding:8px 10px;border-radius:9px;background:var(--soft)}.setting{display:grid;grid-template-columns:minmax(130px,1fr) minmax(150px,1fr);gap:9px;align-items:center;padding:9px 0;border-bottom:1px solid var(--line)}.setting:last-child{border-bottom:0}.setting label{font-weight:650}.setting small{display:block;color:var(--muted);font-weight:400}.setting input,.setting select{width:100%;border:1px solid var(--line);border-radius:8px;padding:8px;background:var(--bg);color:inherit}.setting input:disabled,.setting select:disabled{opacity:.6}.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}.actions button{border:1px solid var(--accent);border-radius:8px;padding:8px 12px;background:var(--accent);color:var(--button);cursor:pointer}.inline-action{border:1px solid var(--accent);border-radius:7px;padding:5px 8px;background:var(--accent);color:var(--button);cursor:pointer;font-size:12px}.actions button:disabled{opacity:.6;cursor:wait}@media(max-width:650px){.shell{padding:10px}.card{padding:14px}.setting{grid-template-columns:1fr}.table-wrap{margin:0 -2px}}
|
||||||
@media(prefers-color-scheme:dark){:root{--bg:#202225;--panel:#2b2e32;--line:#474c52;--ink:#eceff2;--muted:#9da3aa;--accent:#c7b07a;--soft:rgba(199,176,122,.17);--good:#83c995;--bad:#ff9a93;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
@media(prefers-color-scheme:dark){:root{--bg:#202225;--panel:#2b2e32;--line:#474c52;--ink:#eceff2;--muted:#9da3aa;--accent:#c7b07a;--soft:rgba(199,176,122,.17);--good:#83c995;--bad:#ff9a93;--button:#241f14;--shadow:0 10px 26px rgba(0,0,0,.34)}}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -14,36 +14,127 @@
|
|||||||
<main class="shell"><section class="card">
|
<main class="shell"><section class="card">
|
||||||
<div class="eyebrow">SHD MCP · controller observability</div>
|
<div class="eyebrow">SHD MCP · controller observability</div>
|
||||||
<h1 id="title">Контроллеры</h1><p class="lede" id="lede"></p>
|
<h1 id="title">Контроллеры</h1><p class="lede" id="lede"></p>
|
||||||
<div class="toolbar"><button id="refresh" class="primary" type="button">Обновить</button><select id="source" hidden><option value="auto">Автоматически</option><option value="native">Нативный источник</option><option value="influx">Influx/Grafana</option><option value="all">Все источники</option></select><select id="preset" hidden><option value="co2-ventilation">CO₂ → вентиляция</option><option value="temperature-heating">Температура → отопление</option><option value="humidity-ventilation">Влажность → вентиляция</option><option value="custom">Своя пара</option></select></div>
|
<div class="toolbar"><button id="refresh" class="primary" type="button">Обновить</button><select id="source" hidden><option value="auto" data-i18n="sourceAuto">Автоматически</option><option value="native" data-i18n="sourceNative">Нативный источник</option><option value="influx" data-i18n="sourceInflux">Influx/Grafana</option><option value="all" data-i18n="sourceAll">Все источники</option></select><select id="preset" hidden><option value="co2-ventilation" data-i18n="presetCo2Ventilation">CO₂ → вентиляция</option><option value="temperature-heating" data-i18n="presetTemperatureHeating">Температура → отопление</option><option value="humidity-ventilation" data-i18n="presetHumidityVentilation">Влажность → вентиляция</option><option value="custom" data-i18n="presetCustom">Своя пара</option></select></div>
|
||||||
<div id="status" class="status" role="status" aria-live="polite"></div><div id="summary" class="summary"></div><div id="content"></div>
|
<div id="status" class="status" role="status" aria-live="polite"></div><div id="summary" class="summary"></div><div id="content"></div>
|
||||||
</section></main>
|
</section></main>
|
||||||
<script>
|
<script>
|
||||||
(function(){'use strict';
|
(function(){'use strict';
|
||||||
var T={ru:{history:['История контроллера','Временные точки и происхождение данных.'],compare:['Сравнение рядов','Универсальное сравнение временных рядов на общей шкале.'],settings:['Экспертные настройки','Поля доступны для записи только при явном разрешении источника.'],topology:['Топология контроллеров','Все контроллеры проекта; inventory устройств загружается отдельно.'],health:['Здоровье telemetry','Состояние нативного источника и Influx/Grafana.'],audit:['Аудит операций','Команды, подтверждения и readback по source-контроллеру.'],inventorySeparate:'Инвентарь устройств загружается отдельным запросом по выбранному контроллеру.',refresh:'Обновить',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',native:'Нативный источник',influx:'Influx/Grafana',available:'доступен',unavailable:'недоступен',configured:'настроен',points:'точек',source:'Источник',time:'Время',value:'Значение',name:'Название',controller:'Контроллер',device:'Устройство',room:'Комната',type:'Тип',status:'Статус',error:'Ошибка',readonly:'Только чтение',save:'Сохранить',saved:'Настройки сохранены.',noWritable:'Нет разрешённых writable-параметров.',required:'Обязательное поле',unknown:'Неизвестный формат ответа.',fail:'Не удалось загрузить данные.',id:'ID',action:'Действие',requested:'Запрошено',actual:'Фактически',revision:'Ревизия',checked:'Проверено'},en:{history:['Controller history','Time-series points and data provenance.'],compare:['Series comparison','Universal time-series comparison on one shared scale.'],settings:['Expert settings','Fields are writable only when the source explicitly allows it.'],topology:['Controller topology','All project controllers; device inventory is loaded separately.'],health:['Telemetry health','Native-source and Influx/Grafana status.'],audit:['Operation audit','Commands, confirmations and readback for the source controller.'],inventorySeparate:'Device inventory is loaded by a separate request for the selected controller.',refresh:'Refresh',loading:'Loading…',empty:'No data to display.',records:'records',native:'Native source',influx:'Influx/Grafana',available:'available',unavailable:'unavailable',configured:'configured',points:'points',source:'Source',time:'Time',value:'Value',name:'Name',controller:'Controller',device:'Device',room:'Room',type:'Type',status:'Status',error:'Error',readonly:'Read-only',save:'Save',saved:'Settings saved.',noWritable:'No writable parameters are allowed.',required:'Required field',unknown:'Unknown response format.',fail:'Could not load data.',id:'ID',action:'Action',requested:'Requested',actual:'Actual',revision:'Revision',checked:'Checked'}};
|
var T={
|
||||||
var state={mode:'history',config:{},data:null,busy:false,lang:(String((window.openai&&window.openai.locale)||document.documentElement.lang||'ru').toLowerCase().indexOf('en')===0?'en':'ru')};var pending=new Map(),nextId=1;
|
ru:{
|
||||||
|
history:['История контроллера','Временные точки и происхождение данных.'],
|
||||||
|
compare:['Сравнение рядов','Универсальное сравнение временных рядов на общей шкале.'],
|
||||||
|
settings:['Экспертные настройки','Поля доступны для записи только при явном разрешении источника.'],
|
||||||
|
topology:['Топология контроллеров','Все контроллеры проекта; устройства source-контроллера открываются отдельным режимом.'],
|
||||||
|
devices:['Устройства контроллера','Инвентарь устройств с текущими значениями и профилем выбранной строки.'],
|
||||||
|
'live-state':['Текущие состояния','Нормализованные состояния устройств с freshness, quality и readback.'],
|
||||||
|
statistics:['Статистика состояния','Серверный расчёт событий, периодов, длительностей и почасовой разбивки.'],
|
||||||
|
health:['Здоровье телеметрии','Состояние нативного источника и Influx/Grafana.'],
|
||||||
|
audit:['Аудит операций','Команды, подтверждения и readback по source-контроллеру.'],
|
||||||
|
inventorySeparate:'Инвентарь устройств загружается отдельным запросом по выбранному контроллеру.',
|
||||||
|
refresh:'Обновить',loading:'Загрузка…',empty:'Нет данных для отображения.',records:'записей',
|
||||||
|
deviceCount:'устройств',controllerCount:'контроллеров',stateCount:'состояний',samples:'выборок',
|
||||||
|
sourceAuto:'Автоматически',sourceNative:'Нативный источник',sourceInflux:'Influx/Grafana',sourceAll:'Все источники',
|
||||||
|
presetCo2Ventilation:'CO₂ → вентиляция',presetTemperatureHeating:'Температура → отопление',presetHumidityVentilation:'Влажность → вентиляция',presetCustom:'Своя пара',
|
||||||
|
native:'Нативный источник',influx:'Influx/Grafana',available:'доступен',unavailable:'недоступен',
|
||||||
|
configured:'настроен',points:'точек',source:'Источник',sourceDetails:'Источник и качество',
|
||||||
|
time:'Время',value:'Значение',name:'Название',controller:'Контроллер',device:'Устройство',
|
||||||
|
room:'Комната',category:'Категория',type:'Тип',status:'Статус',error:'Ошибка',readonly:'Только чтение',
|
||||||
|
save:'Сохранить',saved:'Настройки сохранены.',noWritable:'Нет разрешённых параметров для записи.',
|
||||||
|
required:'Обязательное поле',unknown:'Неизвестный формат ответа.',fail:'Не удалось загрузить данные.',
|
||||||
|
id:'ID',action:'Действие',requested:'Запрошено',actual:'Фактически',revision:'Ревизия',checked:'Проверено',
|
||||||
|
details:'Подробнее',profile:'Профиль устройства',state:'Состояние',quality:'Качество',
|
||||||
|
freshness:'Свежесть',readback:'Readback',access:'Доступ',unit:'Единица',kind:'Тип данных',
|
||||||
|
events:'событий',transitions:'переключений',periods:'периодов',activeTime:'активное время',
|
||||||
|
activeShare:'доля активности',averageDuration:'средняя длительность',medianDuration:'медиана',
|
||||||
|
shortest:'самый короткий',longest:'самый длинный',coverage:'охват',intervals:'Периоды активности',
|
||||||
|
hourly:'По часам',bucket:'Час',duration:'Длительность',start:'Начало',end:'Конец',
|
||||||
|
second:'с',minute:'мин',hour:'ч',day:'д',binary:'бинарный',numeric:'числовой',
|
||||||
|
minimum:'минимум',maximum:'максимум',average:'среднее',open:'открытый',
|
||||||
|
yes:'да',no:'нет',truncated:'выборка ограничена',noStats:'Статистика недоступна для выбранного диапазона или источника.',
|
||||||
|
noItems:'Устройства не найдены.',unavailableReason:'Причина недоступности'
|
||||||
|
},
|
||||||
|
en:{
|
||||||
|
history:['Controller history','Time-series points and data provenance.'],
|
||||||
|
compare:['Series comparison','Universal time-series comparison on one shared scale.'],
|
||||||
|
settings:['Expert settings','Fields are writable only when the source explicitly allows it.'],
|
||||||
|
topology:['Controller topology','All project controllers; source-controller devices open in a separate mode.'],
|
||||||
|
devices:['Controller devices','Device inventory with current values and a profile action for the selected row.'],
|
||||||
|
'live-state':['Current live state','Normalized device states with freshness, quality and readback.'],
|
||||||
|
statistics:['State statistics','Server-computed events, periods, durations and hourly buckets.'],
|
||||||
|
health:['Telemetry health','Native-source and Influx/Grafana status.'],
|
||||||
|
audit:['Operation audit','Commands, confirmations and readback for the source controller.'],
|
||||||
|
inventorySeparate:'Device inventory is loaded by a separate request for the selected controller.',
|
||||||
|
refresh:'Refresh',loading:'Loading…',empty:'No data to display.',records:'records',
|
||||||
|
deviceCount:'devices',controllerCount:'controllers',stateCount:'states',samples:'samples',
|
||||||
|
sourceAuto:'Automatic',sourceNative:'Native source',sourceInflux:'Influx/Grafana',sourceAll:'All sources',
|
||||||
|
presetCo2Ventilation:'CO₂ → ventilation',presetTemperatureHeating:'Temperature → heating',presetHumidityVentilation:'Humidity → ventilation',presetCustom:'Custom pair',
|
||||||
|
native:'Native source',influx:'Influx/Grafana',available:'available',unavailable:'unavailable',
|
||||||
|
configured:'configured',points:'points',source:'Source',sourceDetails:'Source and quality',
|
||||||
|
time:'Time',value:'Value',name:'Name',controller:'Controller',device:'Device',
|
||||||
|
room:'Room',category:'Category',type:'Type',status:'Status',error:'Error',readonly:'Read-only',
|
||||||
|
save:'Save',saved:'Settings saved.',noWritable:'No writable parameters are allowed.',
|
||||||
|
required:'Required field',unknown:'Unknown response format.',fail:'Could not load data.',
|
||||||
|
id:'ID',action:'Action',requested:'Requested',actual:'Actual',revision:'Revision',checked:'Checked',
|
||||||
|
details:'Details',profile:'Device profile',state:'State',quality:'Quality',
|
||||||
|
freshness:'Freshness',readback:'Readback',access:'Access',unit:'Unit',kind:'Data type',
|
||||||
|
events:'events',transitions:'transitions',periods:'periods',activeTime:'active time',
|
||||||
|
activeShare:'active share',averageDuration:'average duration',medianDuration:'median',
|
||||||
|
shortest:'shortest',longest:'longest',coverage:'coverage',intervals:'Activity periods',
|
||||||
|
hourly:'Hourly',bucket:'Hour',duration:'Duration',start:'Start',end:'End',
|
||||||
|
second:'s',minute:'min',hour:'h',day:'d',binary:'binary',numeric:'numeric',
|
||||||
|
minimum:'minimum',maximum:'maximum',average:'average',open:'open',
|
||||||
|
yes:'yes',no:'no',truncated:'sample limited',noStats:'Statistics are unavailable for this range or source.',
|
||||||
|
noItems:'No devices found.',unavailableReason:'Unavailable reason'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var state={mode:'history',config:{},data:null,busy:false,lang:(String((window.openai&&window.openai.locale)||document.documentElement.lang||'ru').toLowerCase().indexOf('en')===0?'en':'ru')};
|
||||||
|
var pending=new Map(),nextId=1;
|
||||||
var el={title:document.getElementById('title'),lede:document.getElementById('lede'),refresh:document.getElementById('refresh'),source:document.getElementById('source'),preset:document.getElementById('preset'),status:document.getElementById('status'),summary:document.getElementById('summary'),content:document.getElementById('content')};
|
var el={title:document.getElementById('title'),lede:document.getElementById('lede'),refresh:document.getElementById('refresh'),source:document.getElementById('source'),preset:document.getElementById('preset'),status:document.getElementById('status'),summary:document.getElementById('summary'),content:document.getElementById('content')};
|
||||||
function t(k){return(T[state.lang]&&T[state.lang][k])||k}function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}function text(x){if(x===null||x===undefined||x==='')return'—';if(typeof x==='object')try{return JSON.stringify(x)}catch(e){return'—'}return String(x)}function esc(x){return text(x).replace(/[&<>"']/g,function(c){return({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]})}function unwrap(x){var p=x&&x.structuredContent?x.structuredContent:(x&&x.toolOutput?x.toolOutput:x);if(p&&p.isError)throw Error(t('fail'));return p||{}}
|
function t(k){return(T[state.lang]&&T[state.lang][k])||k}
|
||||||
function merge(a,b){return Object.assign({},a||{},b||{})}function setStatus(v,bad){el.status.textContent=v||'';el.status.className=bad?'status error':'status'}function pill(v,kind){return '<span class="pill '+(kind||'')+'">'+esc(v)+'</span>'}function clear(){el.content.replaceChildren();el.summary.replaceChildren()}
|
function obj(x){return x&&typeof x==='object'&&!Array.isArray(x)}
|
||||||
|
function text(x){if(x===null||x===undefined||x==='')return'—';if(typeof x==='object')try{return JSON.stringify(x)}catch(e){return'—'}return String(x)}
|
||||||
|
function esc(x){return text(x).replace(/[&<>"']/g,function(c){return({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]})}
|
||||||
|
function unwrap(x){var p=x&&x.structuredContent?x.structuredContent:(x&&x.toolOutput?x.toolOutput:x);if(p&&p.isError)throw Error(t('fail'));return p||{}}
|
||||||
|
function callData(x){var p=unwrap(x);return p&&obj(p.data)&&!p.schema?p.data:p}
|
||||||
|
function merge(a,b){return Object.assign({},a||{},b||{})}
|
||||||
|
function setStatus(v,bad){el.status.textContent=v||'';el.status.className=bad?'status error':'status'}
|
||||||
|
function pill(v,kind){return '<span class="pill '+(kind||'')+'">'+esc(v)+'</span>'}
|
||||||
|
function clear(){el.content.replaceChildren();el.summary.replaceChildren()}
|
||||||
function rpc(method,params){var o=window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=setTimeout(function(){pending.delete(id);reject(Error(t('fail')))},15000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
function rpc(method,params){var o=window.openai;if(method==='tools/call'&&o&&typeof o.callTool==='function')return Promise.resolve(o.callTool(params.name,params.arguments||{}));var id=nextId++;window.parent.postMessage({jsonrpc:'2.0',id:id,method:method,params:params||{}},'*');return new Promise(function(resolve,reject){var timeout=setTimeout(function(){pending.delete(id);reject(Error(t('fail')))},15000);pending.set(id,{resolve:resolve,reject:reject,timeout:timeout})})}
|
||||||
function metaFrom(p){var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{};state.mode=String(w.mode||m.mode||state.mode||'history');state.config=merge(state.config,w);state.config.sourceArgs=obj(w.sourceArgs)?w.sourceArgs:(obj(m.sourceArgs)?m.sourceArgs:{});return m}
|
function metaFrom(p){var m=obj(p.meta)?p.meta:{},w=obj(m.widget)?m.widget:{};state.mode=String(w.mode||m.mode||state.mode||'history');state.config=merge(state.config,w);if(obj(w.sourceArgs)&&Object.keys(w.sourceArgs).length)state.config.sourceArgs=w.sourceArgs;else if(obj(m.sourceArgs)&&Object.keys(m.sourceArgs).length)state.config.sourceArgs=m.sourceArgs;return m}
|
||||||
function dataFrom(p){var source=unwrap(p);metaFrom(source);return source.meta&&source.meta.widget&&source.data!==undefined?source.data:source}
|
function dataFrom(p){var source=unwrap(p);metaFrom(source);return source.meta&&source.meta.widget&&source.data!==undefined?source.data:source}
|
||||||
function labels(){var heading=T[state.lang][state.mode]||T[state.lang].history;el.title.textContent=heading[0];el.lede.textContent=heading[1];el.refresh.textContent=t('refresh');el.source.hidden=!['history','compare','health'].includes(state.mode);el.preset.hidden=state.mode!=='compare';if(state.mode==='compare'&&state.config.preset)el.preset.value=state.config.preset}
|
function translateOptions(select){select.querySelectorAll('[data-i18n]').forEach(function(option){option.textContent=t(option.getAttribute('data-i18n'))})}
|
||||||
function summary(items){el.summary.replaceChildren();(items||[]).forEach(function(x){var s=document.createElement('span');s.className='pill '+(x.kind||'');s.textContent=x.value;el.summary.append(s)})}
|
function labels(){var heading=T[state.lang][state.mode]||T[state.lang].history;document.documentElement.lang=state.lang;el.title.textContent=heading[0];el.lede.textContent=heading[1];el.refresh.textContent=t('refresh');translateOptions(el.source);translateOptions(el.preset);el.source.hidden=!['history','compare','health','statistics'].includes(state.mode);el.preset.hidden=state.mode!=='compare';if(state.mode==='compare'&&state.config.preset)el.preset.value=state.config.preset;if(state.config.sourceArgs&&state.config.sourceArgs.source)el.source.value=state.config.sourceArgs.source}
|
||||||
|
function summary(items){el.summary.replaceChildren();(items||[]).forEach(function(x){var s=document.createElement('span');s.className='pill '+(x.kind||'');s.textContent=text(x.value);el.summary.append(s)})}
|
||||||
|
function formatNumber(value){var number=Number(value);if(value===null||value===undefined||value===''||!Number.isFinite(number))return text(value);return number.toLocaleString(state.lang==='ru'?'ru-RU':'en-US',{maximumFractionDigits:2})}
|
||||||
|
function formatDuration(value){var seconds=Number(value);if(!Number.isFinite(seconds))return text(value);seconds=Math.max(0,Math.round(seconds));var parts=[];if(seconds>=86400){parts.push(Math.floor(seconds/86400)+' '+t('day'));seconds%=86400}if(seconds>=3600){parts.push(Math.floor(seconds/3600)+' '+t('hour'));seconds%=3600}if(seconds>=60){parts.push(Math.floor(seconds/60)+' '+t('minute'));seconds%=60}if(seconds||parts.length===0)parts.push(seconds+' '+t('second'));return parts.join(' ')}
|
||||||
|
function formatFreshness(value){var ms=Number(value);return Number.isFinite(ms)?formatDuration(ms/1000):text(value)}
|
||||||
|
function sourceLabel(value){return value==='native'?t('native'):value==='influx'?t('influx'):text(value)}
|
||||||
|
function kindLabel(value){return value==='binary'?t('binary'):value==='numeric'?t('numeric'):text(value)}
|
||||||
|
function boolLabel(value){return value?t('yes'):t('no')}
|
||||||
function chart(points,cls){var nums=(Array.isArray(points)?points:[]).map(function(p){return Number(p&&p.value)}).filter(function(v){return Number.isFinite(v)});if(!nums.length)return '<div class="chart-empty">'+esc(t('empty'))+'</div>';var w=700,h=112,pad=18,min=Math.min.apply(null,nums),max=Math.max.apply(null,nums);if(min===max){min-=1;max+=1}var coords=nums.map(function(v,i){var x=pad+(w-pad*2)*(nums.length===1?.5:i/(nums.length-1));var y=h-pad-(h-pad*2)*(v-min)/(max-min);return x.toFixed(1)+','+y.toFixed(1)}).join(' ');return '<svg viewBox="0 0 '+w+' '+h+'" role="img" aria-label="'+esc(t('value'))+'"><polyline class="'+(cls||'line-a')+'" points="'+coords+'"/><text class="axis" x="4" y="14">'+esc(String(max))+'</text><text class="axis" x="4" y="'+(h-3)+'">'+esc(String(min))+'</text></svg>'}
|
function chart(points,cls){var nums=(Array.isArray(points)?points:[]).map(function(p){return Number(p&&p.value)}).filter(function(v){return Number.isFinite(v)});if(!nums.length)return '<div class="chart-empty">'+esc(t('empty'))+'</div>';var w=700,h=112,pad=18,min=Math.min.apply(null,nums),max=Math.max.apply(null,nums);if(min===max){min-=1;max+=1}var coords=nums.map(function(v,i){var x=pad+(w-pad*2)*(nums.length===1?.5:i/(nums.length-1));var y=h-pad-(h-pad*2)*(v-min)/(max-min);return x.toFixed(1)+','+y.toFixed(1)}).join(' ');return '<svg viewBox="0 0 '+w+' '+h+'" role="img" aria-label="'+esc(t('value'))+'"><polyline class="'+(cls||'line-a')+'" points="'+coords+'"/><text class="axis" x="4" y="14">'+esc(String(max))+'</text><text class="axis" x="4" y="'+(h-3)+'">'+esc(String(min))+'</text></svg>'}
|
||||||
function card(title,body){var n=document.createElement('section');n.className='panel';var h=document.createElement('h2');h.textContent=title;n.append(h);if(typeof body==='string')n.insertAdjacentHTML('beforeend',body);else if(body)n.append(body);return n}
|
function card(title,body){var n=document.createElement('section');n.className='panel';var h=document.createElement('h2');h.textContent=title;n.append(h);if(typeof body==='string')n.insertAdjacentHTML('beforeend',body);else if(body)n.append(body);return n}
|
||||||
function table(headers,rows){var wrap=document.createElement('div');wrap.className='table-wrap';var tab=document.createElement('table'),thead=document.createElement('thead'),tr=document.createElement('tr');headers.forEach(function(h){var th=document.createElement('th');th.textContent=h;tr.append(th)});thead.append(tr);tab.append(thead);var tb=document.createElement('tbody');(rows||[]).forEach(function(row){var r=document.createElement('tr');row.forEach(function(v){var td=document.createElement('td');td.textContent=text(v);r.append(td)});tb.append(r)});tab.append(tb);wrap.append(tab);return wrap}
|
function table(headers,rows){var wrap=document.createElement('div');wrap.className='table-wrap';var tab=document.createElement('table'),thead=document.createElement('thead'),tr=document.createElement('tr');headers.forEach(function(h){var th=document.createElement('th');th.textContent=h;tr.append(th)});thead.append(tr);tab.append(thead);var tb=document.createElement('tbody');(rows||[]).forEach(function(row){var r=document.createElement('tr');row.forEach(function(v){var td=document.createElement('td');if(v&&v.nodeType)td.append(v);else td.textContent=text(v);r.append(td)});tb.append(r)});tab.append(tb);wrap.append(tab);return wrap}
|
||||||
function renderHistory(d){var points=Array.isArray(d.points)?d.points:[];var sources=Array.isArray(d.history_sources)?d.history_sources:[];summary([{value:points.length+' '+t('points')},{value:t('source')+': '+text(d.selected_history_source||'—'),kind:d.selected_history_source?'good':'bad'},{value:t('records')+': '+((d.commands||[]).length)}]);var box=document.createElement('div');box.className='grid';box.append(card(t('source'),'<div class="source-list">'+sources.map(function(s){return '<div class="source-row"><span>'+esc(s.name==='native'?t('native'):t('influx'))+'</span><span>'+pill(s.available?t('available'):t('unavailable'),s.available?'good':'bad')+'</span></div>'}).join('')+'</div>'));var cp=card(t('value'));cp.insertAdjacentHTML('beforeend','<div class="chart">'+chart(points,'line-a')+'</div>');box.append(cp);el.content.append(box);if(points.length)el.content.append(table([t('time'),t('value'),t('name'),t('source')],points.slice(-80).reverse().map(function(p){return[p.time,p.value,p.name||p.topic||p.channel,d.telemetry&&d.telemetry.provider||d.selected_history_source]})));else el.content.append(document.createTextNode(t('empty')))}
|
function empty(){var n=document.createElement('div');n.className='empty';n.textContent=t('empty');return n}
|
||||||
function renderCompare(d){var list=Array.isArray(d.series)?d.series:[];summary([{value:(d.preset||'custom')},{value:list.length+' '+t('records')},{value:text(d.from)+' → '+text(d.to)}]);var grid=document.createElement('div');grid.className='grid';list.forEach(function(s,i){var source=s.selected_history_source||s.history_source||'';var sourceLabel=source==='native'?t('native'):(source==='influx'?t('influx'):text(source||'—'));var p=card(s.label||s.id||t('records'),'<p>'+esc(s.controller||'')+' · '+esc(s.item_id||'')+' '+pill(sourceLabel,source?'':'bad')+' '+pill(s.available?t('available'):text(s.error_code||t('unavailable')),s.available?'good':'bad')+'</p><div class="chart">'+chart(s.points,i%2?'line-b':'line-a')+'</div>');grid.append(p)});el.content.append(grid);if(!list.length)el.content.append(document.createTextNode(t('empty')))}
|
function metricGrid(items){var grid=document.createElement('div');grid.className='grid';items.forEach(function(item){grid.append(card(item[0],'<p>'+esc(item[1])+'</p>'))});return grid}
|
||||||
|
function renderHistory(d){var points=Array.isArray(d.points)?d.points:[];var sources=Array.isArray(d.history_sources)?d.history_sources:[];summary([{value:points.length+' '+t('points')},{value:t('source')+': '+text(d.selected_history_source||'—'),kind:d.selected_history_source?'good':'bad'},{value:t('records')+': '+((d.commands||[]).length)}]);var box=document.createElement('div');box.className='grid';box.append(card(t('source'),'<div class="source-list">'+sources.map(function(s){return '<div class="source-row"><span>'+esc(sourceLabel(s.name))+'</span><span>'+pill(s.available?t('available'):t('unavailable'),s.available?'good':'bad')+'</span></div>'}).join('')+'</div>'));var cp=card(t('value'));cp.insertAdjacentHTML('beforeend','<div class="chart">'+chart(points,'line-a')+'</div>');box.append(cp);el.content.append(box);if(points.length)el.content.append(table([t('time'),t('value'),t('name'),t('source')],points.slice(-80).reverse().map(function(p){return[p.time,p.value,p.name||p.topic||p.channel,d.telemetry&&d.telemetry.provider||d.selected_history_source]})));else el.content.append(empty())}
|
||||||
|
function renderCompare(d){var list=Array.isArray(d.series)?d.series:[];summary([{value:(d.preset||'custom')},{value:list.length+' '+t('records')},{value:text(d.from)+' → '+text(d.to)}]);var grid=document.createElement('div');grid.className='grid';list.forEach(function(s,i){var source=s.selected_history_source||s.history_source||'';var p=card(s.label||s.id||t('records'),'<p>'+esc(s.controller||'')+' · '+esc(s.item_id||'')+' '+pill(sourceLabel(source),source?'':'bad')+' '+pill(s.available?t('available'):text(s.error_code||t('unavailable')),s.available?'good':'bad')+'</p><div class="chart">'+chart(s.points,i%2?'line-b':'line-a')+'</div>');grid.append(p)});el.content.append(grid);if(!list.length)el.content.append(empty())}
|
||||||
function inputFor(s){var wrap=document.createElement('div');wrap.className='setting';var label=document.createElement('label');label.textContent=text(s.name||s.key);if(s.description){var small=document.createElement('small');small.textContent=s.description;label.append(small)}var value=s.current_value;var input;if(Array.isArray(s.enum_values)&&s.enum_values.length){input=document.createElement('select');s.enum_values.forEach(function(v){var o=document.createElement('option');o.value=String(v);o.textContent=String(v);input.append(o)});input.value=value===null||value===undefined?'':String(value)}else if(String(s.value_type||'').toLowerCase()==='boolean'){input=document.createElement('input');input.type='checkbox';input.checked=value===true||value===1||value==='1'}else{input=document.createElement('input');input.type=['number','integer'].includes(String(s.value_type||'').toLowerCase())?'number':'text';if(s.min!==null&&s.min!==undefined)input.min=s.min;if(s.max!==null&&s.max!==undefined)input.max=s.max;if(s.step!==null&&s.step!==undefined)input.step=s.step;input.value=value===null||value===undefined?'':String(value)}input.dataset.key=s.key;input.disabled=!s.writable;wrap.append(label,input);if(!s.writable){var ro=document.createElement('div');ro.className='muted';ro.textContent=t('readonly');wrap.append(ro)}return wrap}
|
function inputFor(s){var wrap=document.createElement('div');wrap.className='setting';var label=document.createElement('label');label.textContent=text(s.name||s.key);if(s.description){var small=document.createElement('small');small.textContent=s.description;label.append(small)}var value=s.current_value;var input;if(Array.isArray(s.enum_values)&&s.enum_values.length){input=document.createElement('select');s.enum_values.forEach(function(v){var o=document.createElement('option');o.value=String(v);o.textContent=String(v);input.append(o)});input.value=value===null||value===undefined?'':String(value)}else if(String(s.value_type||'').toLowerCase()==='boolean'){input=document.createElement('input');input.type='checkbox';input.checked=value===true||value===1||value==='1'}else{input=document.createElement('input');input.type=['number','integer'].includes(String(s.value_type||'').toLowerCase())?'number':'text';if(s.min!==null&&s.min!==undefined)input.min=s.min;if(s.max!==null&&s.max!==undefined)input.max=s.max;if(s.step!==null&&s.step!==undefined)input.step=s.step;input.value=value===null||value===undefined?'':String(value)}input.dataset.key=s.key;input.disabled=!s.writable;wrap.append(label,input);if(!s.writable){var ro=document.createElement('div');ro.className='muted';ro.textContent=t('readonly');wrap.append(ro)}return wrap}
|
||||||
function renderSettings(d){var settings=Array.isArray(d.settings)?d.settings:[];summary([{value:settings.length+' '+t('records')},{value:t('revision')+': '+text(d.source_revision)}]);var form=document.createElement('div');form.className='panel';settings.forEach(function(s){form.append(inputFor(s))});var actions=document.createElement('div');actions.className='actions';var btn=document.createElement('button');btn.type='button';btn.textContent=t('save');btn.disabled=!settings.some(function(s){return s.writable});btn.onclick=function(){saveSettings(form,settings,d)};actions.append(btn);form.append(actions);if(!settings.some(function(s){return s.writable})){var note=document.createElement('p');note.className='muted';note.textContent=t('noWritable');form.append(note)}el.content.append(form)}
|
function renderSettings(d){var settings=Array.isArray(d.settings)?d.settings:[];summary([{value:settings.length+' '+t('records')},{value:t('revision')+': '+text(d.source_revision)}]);var form=document.createElement('div');form.className='panel';settings.forEach(function(s){form.append(inputFor(s))});var actions=document.createElement('div');actions.className='actions';var btn=document.createElement('button');btn.type='button';btn.textContent=t('save');btn.disabled=!settings.some(function(s){return s.writable});btn.onclick=function(){saveSettings(form,settings,d)};actions.append(btn);form.append(actions);if(!settings.some(function(s){return s.writable})){var note=document.createElement('p');note.className='muted';note.textContent=t('noWritable');form.append(note)}el.content.append(form)}
|
||||||
function saveSettings(form,settings,d){var changes={};settings.forEach(function(s){if(!s.writable)return;var input=form.querySelector('[data-key="'+CSS.escape(String(s.key))+'"]');if(!input)return;var v=input.type==='checkbox'?input.checked:input.value;if(['number','integer'].includes(String(s.value_type||'').toLowerCase())&&v!=='')v=Number(v);changes[s.key]=v});if(!Object.keys(changes).length)return;var a=merge(state.config.sourceArgs,{settings:changes,expected_revision:d.source_revision||'',idempotency_key:'mcp-widget-'+Date.now()});rpc('tools/call',{name:state.config.writeTool||'shd_update_controller_expert_settings',arguments:a}).then(function(){setStatus(t('saved'),false);refresh()}).catch(function(e){setStatus(e.message||t('fail'),true)})}
|
function saveSettings(form,settings,d){var changes={};settings.forEach(function(s){if(!s.writable)return;var input=form.querySelector('[data-key="'+CSS.escape(String(s.key))+'"]');if(!input)return;var v=input.type==='checkbox'?input.checked:input.value;if(['number','integer'].includes(String(s.value_type||'').toLowerCase())&&v!=='')v=Number(v);changes[s.key]=v});if(!Object.keys(changes).length)return;var a=merge(state.config.sourceArgs,{settings:changes,expected_revision:d.source_revision||'',idempotency_key:'mcp-widget-'+Date.now()});rpc('tools/call',{name:state.config.writeTool||'shd_update_controller_expert_settings',arguments:a}).then(function(){setStatus(t('saved'),false);refresh()}).catch(function(e){setStatus(e.message||t('fail'),true)})}
|
||||||
function renderTopology(d){var controllers=Array.isArray(d.controllers)?d.controllers:[];summary([{value:controllers.length+' '+t('controller')},{value:t('inventorySeparate')}]);el.content.append(table([t('controller'),t('type'),t('revision')],controllers.map(function(c){var role=c.controller_role==='source'?t('source'):t('controller');return[c.controller_name||c.controller,role+' · '+(c.controller_type||'—'),c.source_revision]})))}
|
function itemValue(item){if(Object.prototype.hasOwnProperty.call(item,'value'))return item.value;if(Object.prototype.hasOwnProperty.call(item,'current_value'))return item.current_value;var states=Array.isArray(item.states)?item.states:[];if(states.length===1)return states[0].value_present?states[0].value:null;return states.length?states.map(function(s){return text(s.value)}).join(', '):null}
|
||||||
function renderHealth(d){var list=Array.isArray(d.controllers)?d.controllers:[d];summary([{value:list.length+' '+t('controller')}]);var grid=document.createElement('div');grid.className='grid';list.forEach(function(c){var sources=Array.isArray(c.sources)?c.sources:[];grid.append(card(c.controller||c.source_controller_id||t('controller'),'<p>'+pill(c.status||'—',c.status==='available'?'good':'')+'</p><div class="source-list">'+sources.map(function(s){return '<div class="source-row"><span>'+esc(s.name==='native'?t('native'):t('influx'))+'</span><span>'+pill(s.available?t('available'):(s.configured?t('configured'):t('unavailable')),s.available?'good':(s.configured?'':'bad'))+'</span></div>'}).join('')+'</div>'))});el.content.append(grid)}
|
function showProfile(item,d){var sourceArgs=obj(state.config.sourceArgs)?state.config.sourceArgs:{};var args={project:sourceArgs.project||d.project||'',controller:sourceArgs.controller||d.controller||'',device:String(item.id||item.device_id||''),include_live:true};if(!args.project||!args.controller||!args.device){setStatus(t('fail'),true);return}setStatus(t('loading'),false);rpc('tools/call',{name:state.config.detailTool||'shd_get_controller_device_profile',arguments:args}).then(function(result){var response=callData(result);var profile=obj(response.profile)?response.profile:response;var panel=card(t('profile'));panel.append(table([t('name'),t('value')],[[t('id'),profile.item_id||response.device||args.device],[t('name'),profile.user_name||profile.technical_name||profile.device_name],[t('room'),profile.room],[t('category'),profile.category],[t('type'),profile.type||profile.value_type],[t('access'),profile.access],[t('revision'),response.source_revision||profile.source_revision],[t('readback'),boolLabel(response.readback===true||profile.readback===true)]]));var states=Array.isArray(profile.states)?profile.states:[];if(states.length)panel.append(card(t('state'),table([t('name'),t('value'),t('unit'),t('quality'),t('readback')],states.map(function(s){return[s.name,s.value,s.unit,s.quality,boolLabel(s.readback)]}))));el.content.prepend(panel);setStatus('',false)}).catch(function(e){setStatus(e.message||t('fail'),true)})}
|
||||||
function renderAudit(d){var rows=Array.isArray(d.audit)?d.audit:(Array.isArray(d.commands)?d.commands:[]);summary([{value:rows.length+' '+t('records')}]);el.content.append(rows.length?table([t('time'),t('device'),t('action'),t('status'),t('requested'),t('actual')],rows.map(function(x){return[x.created_at,x.item_id,x.action,x.status,text(x.requested_value),text(x.actual_readback)]})):document.createTextNode(t('empty')))}
|
function renderDevices(d){var items=Array.isArray(d.items)?d.items:[];var total=Number.isFinite(Number(d.total))?d.total:items.length;summary([{value:total+' '+t('deviceCount')},{value:t('source')+': '+sourceLabel(d.source_type||d.source),kind:d.inventory_available?'good':'bad'},{value:t('revision')+': '+text(d.inventory_revision||d.revision)}]);if(!items.length){el.content.append(empty());return}var rows=items.map(function(item){var detail=document.createElement('button');detail.type='button';detail.className='inline-action';detail.textContent=t('details');detail.onclick=function(){showProfile(item,d)};return[item.id,item.name||item.title||item.device_name,item.room,item.category,item.type||item.value_type,item.access,itemValue(item),detail]});el.content.append(table([t('id'),t('name'),t('room'),t('category'),t('type'),t('access'),t('value'),t('details')],rows))}
|
||||||
function render(){clear();labels();var d=state.data||{};try{if(state.mode==='history')renderHistory(d);else if(state.mode==='compare')renderCompare(d);else if(state.mode==='settings')renderSettings(d);else if(state.mode==='topology')renderTopology(d);else if(state.mode==='health')renderHealth(d);else if(state.mode==='audit')renderAudit(d);else el.content.append(document.createTextNode(t('unknown')))}catch(e){setStatus(e.message||t('unknown'),true)}}
|
function liveRows(d){var rows=[];var controllers=Array.isArray(d.controllers)?d.controllers:[];if(controllers.length){controllers.forEach(function(c){(Array.isArray(c.states)?c.states:[]).forEach(function(s){rows.push([c.controller_name||c.controller,s.item_id,s.name,s.value_present?s.value:null,s.unit,s.quality,formatFreshness(s.freshness_ms),boolLabel(s.readback),s.unavailable_reason])})})}else{(Array.isArray(d.states)?d.states:[]).forEach(function(s){rows.push([s.source_controller_id,s.item_id,s.name,s.value_present?s.value:null,s.unit,s.quality,formatFreshness(s.freshness_ms),boolLabel(s.readback),s.unavailable_reason])})}return rows}
|
||||||
|
function renderLiveState(d){var rows=liveRows(d);var controllers=Array.isArray(d.controllers)?d.controllers:[];var controllerCount=d.controller_count||controllers.length||new Set(rows.map(function(r){return r[0]})).size;summary([{value:controllerCount+' '+t('controllerCount')},{value:(d.state_count||rows.length)+' '+t('stateCount')},{value:t('readback')+': '+boolLabel(d.readback)}]);el.content.append(rows.length?table([t('controller'),t('id'),t('name'),t('value'),t('unit'),t('quality'),t('freshness'),t('readback'),t('unavailableReason')],rows):empty())}
|
||||||
|
function renderStatistics(d){var s=obj(d.summary)?d.summary:{};var available=d.statistics_available===true;summary([{value:kindLabel(d.statistics_kind)},{value:(s.sample_count||0)+' '+t('samples')},{value:t('source')+': '+sourceLabel(d.selected_history_source||d.history_source_requested),kind:d.selected_history_source?'good':'bad'},{value:text(d.from)+' → '+text(d.to)}]);if(!available){var unavailable=card(t('status'));unavailable.append(document.createTextNode(t('noStats')+' '+text(d.statistics_error_code||'')));el.content.append(unavailable)}else if(d.statistics_kind==='binary'){el.content.append(metricGrid([[t('events'),formatNumber(s.event_count)], [t('transitions'),formatNumber(s.transition_count)], [t('periods'),formatNumber(s.period_count)], [t('activeTime'),formatDuration(s.active_seconds)], [t('activeShare'),formatNumber(s.active_percent)+'%'], [t('coverage'),formatDuration(s.coverage_seconds)], [t('averageDuration'),formatDuration(s.average_duration_seconds)], [t('medianDuration'),formatDuration(s.median_duration_seconds)], [t('shortest'),formatDuration(s.min_duration_seconds)], [t('longest'),formatDuration(s.max_duration_seconds)]]))}else if(d.statistics_kind==='numeric'){el.content.append(metricGrid([[t('samples'),formatNumber(s.sample_count)], [t('minimum'),formatNumber(s.min)], [t('maximum'),formatNumber(s.max)], [t('average'),formatNumber(s.average)], [t('median'),formatNumber(s.median)]]))}var meta=card(t('sourceDetails'));meta.append(table([t('source'),t('kind'),t('quality'),t('unit'),t('start'),t('end')],[[sourceLabel(d.selected_history_source||d.history_source_requested),kindLabel(d.statistics_kind),d.quality,d.value_unit||'—',d.from,d.to]]));el.content.append(meta);var intervals=Array.isArray(d.intervals)?d.intervals:[];if(intervals.length){el.content.append(card(t('intervals'),table([t('start'),t('end'),t('duration'),t('status')],intervals.map(function(i){return[i.start,i.end,formatDuration(i.duration_seconds),i.open?t('open'):t('checked')]}))))}var buckets=Array.isArray(d.buckets)?d.buckets:[];if(buckets.length){var headers=d.statistics_kind==='binary'?[t('bucket'),t('events'),t('transitions'),t('periods'),t('activeTime')]:[t('bucket'),t('samples'),t('minimum'),t('maximum'),t('average')];el.content.append(card(t('hourly'),table(headers,buckets.map(function(b){return d.statistics_kind==='binary'?[b.bucket_start,formatNumber(b.event_count),formatNumber(b.transition_count),formatNumber(b.period_count),formatDuration(b.active_seconds)]:[b.bucket_start,formatNumber(b.sample_count),formatNumber(b.min),formatNumber(b.max),formatNumber(b.average)]}))));}var points=Array.isArray(d.points)?d.points:[];if(points.length)el.content.append(card(t('points'),table([t('time'),t('value'),t('quality')],points.slice(-80).reverse().map(function(p){return[p.time||p.source_timestamp,p.value,p.quality]}))))}
|
||||||
|
function renderTopology(d){var controllers=Array.isArray(d.controllers)?d.controllers:[];summary([{value:controllers.length+' '+t('controllerCount')},{value:t('inventorySeparate')}]);el.content.append(controllers.length?table([t('controller'),t('type'),t('revision')],controllers.map(function(c){var role=c.controller_role==='source'?t('source'):t('controller');return[c.controller_name||c.controller,role+' · '+(c.controller_type||'—'),c.source_revision]})):empty())}
|
||||||
|
function renderHealth(d){var list=Array.isArray(d.controllers)?d.controllers:[d];summary([{value:list.length+' '+t('controllerCount')}]);var grid=document.createElement('div');grid.className='grid';list.forEach(function(c){var sources=Array.isArray(c.sources)?c.sources:[];grid.append(card(c.controller||c.source_controller_id||t('controller'),'<p>'+pill(c.status||'—',c.status==='available'?'good':'')+'</p><div class="source-list">'+sources.map(function(s){return '<div class="source-row"><span>'+esc(sourceLabel(s.name))+'</span><span>'+pill(s.available?t('available'):(s.configured?t('configured'):t('unavailable')),s.available?'good':(s.configured?'':'bad'))+'</span></div>'}).join('')+'</div>'))});el.content.append(grid)}
|
||||||
|
function renderAudit(d){var rows=Array.isArray(d.audit)?d.audit:(Array.isArray(d.commands)?d.commands:[]);summary([{value:rows.length+' '+t('records')}]);el.content.append(rows.length?table([t('time'),t('device'),t('action'),t('status'),t('requested'),t('actual')],rows.map(function(x){return[x.created_at,x.item_id,x.action,x.status,text(x.requested_value),text(x.actual_readback)]})):empty())}
|
||||||
|
function render(){clear();labels();var d=state.data||{};try{if(state.mode==='history')renderHistory(d);else if(state.mode==='compare')renderCompare(d);else if(state.mode==='settings')renderSettings(d);else if(state.mode==='topology')renderTopology(d);else if(state.mode==='devices')renderDevices(d);else if(state.mode==='live-state')renderLiveState(d);else if(state.mode==='statistics')renderStatistics(d);else if(state.mode==='health')renderHealth(d);else if(state.mode==='audit')renderAudit(d);else el.content.append(document.createTextNode(t('unknown')))}catch(e){setStatus(e.message||t('unknown'),true)}}
|
||||||
function apply(x){var p=unwrap(x);state.data=dataFrom(p);setStatus('',false);render()}
|
function apply(x){var p=unwrap(x);state.data=dataFrom(p);setStatus('',false);render()}
|
||||||
function refresh(){if(state.busy)return;var args=merge(state.config.listArgs,state.config.sourceArgs);if(state.mode!=='settings'&&el.source.value&&['history','compare','health'].includes(state.mode))args.source=el.source.value;if(state.mode==='compare'){args.preset=el.preset.value;var groups={"co2-ventilation":["co2","ventilation"],"temperature-heating":["temperature","heating"],"humidity-ventilation":["humidity","ventilation"]}[args.preset];if(groups&&Array.isArray(args.series)){var selected=args.series.filter(function(s){return groups.includes(String(s.group||'').toLowerCase())});if(selected.length)args.series=selected}}var tool=state.config.dataTool;if(!tool){setStatus(t('unknown'),true);return}state.busy=true;el.refresh.disabled=true;setStatus(t('loading'),false);rpc('tools/call',{name:tool,arguments:args}).then(apply).catch(function(e){setStatus(e.message||t('fail'),true)}).finally(function(){state.busy=false;el.refresh.disabled=false})}
|
function refresh(){if(state.busy)return;var args=merge(state.config.listArgs,state.config.sourceArgs);if(el.source.value&&['history','compare','health','statistics'].includes(state.mode))args.source=el.source.value;if(state.mode==='compare'){args.preset=el.preset.value;var groups={"co2-ventilation":["co2","ventilation"],"temperature-heating":["temperature","heating"],"humidity-ventilation":["humidity","ventilation"]}[args.preset];if(groups&&Array.isArray(args.series)){var selected=args.series.filter(function(s){return groups.includes(String(s.group||'').toLowerCase())});if(selected.length)args.series=selected}}var tool=state.config.dataTool;if(!tool){setStatus(t('unknown'),true);return}state.busy=true;el.refresh.disabled=true;setStatus(t('loading'),false);rpc('tools/call',{name:tool,arguments:args}).then(apply).catch(function(e){setStatus(e.message||t('fail'),true)}).finally(function(){state.busy=false;el.refresh.disabled=false})}
|
||||||
el.source.onchange=refresh;el.preset.onchange=refresh;el.refresh.onclick=refresh;
|
labels();el.source.onchange=refresh;el.preset.onchange=refresh;el.refresh.onclick=refresh;
|
||||||
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data||{};if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('fail')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'||m.method==='ui/notifications/tool-result'){try{apply(m.params&&m.params.structuredContent||m.params||{})}catch(e){setStatus(e.message||t('fail'),true)}}});
|
window.addEventListener('message',function(ev){if(ev.source!==window.parent)return;var m=ev.data||{};if(m.id!==undefined&&pending.has(m.id)){var q=pending.get(m.id);pending.delete(m.id);clearTimeout(q.timeout);if(m.error)q.reject(Error(m.error.message||t('fail')));else q.resolve(m.result);return}if(m.method==='ui/notifications/tool-input'||m.method==='ui/notifications/tool-result'){try{apply(m.params&&m.params.structuredContent||m.params||{})}catch(e){setStatus(e.message||t('fail'),true)}}});
|
||||||
rpc('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD controller observability',title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){window.parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/initialized',params:{}},'*')}).catch(function(){});var initial=window.openai&&window.openai.toolOutput||window.openai&&window.openai.toolInput;if(initial){try{apply(initial)}catch(e){setStatus(e.message||t('fail'),true)}}else{refresh()}
|
rpc('ui/initialize',{protocolVersion:'2025-11-25',appInfo:{name:'SHD controller observability',title:'SHD MCP',version:'1.0.0',websiteUrl:'https://shd.xyz.su'},appCapabilities:{}}).then(function(){window.parent.postMessage({jsonrpc:'2.0',method:'ui/notifications/initialized',params:{}},'*')}).catch(function(){});var initial=window.openai&&window.openai.toolOutput||window.openai&&window.openai.toolInput;if(initial){try{apply(initial)}catch(e){setStatus(e.message||t('fail'),true)}}else{refresh()}
|
||||||
}());
|
}());
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 22446,
|
"bytes": 36142,
|
||||||
"gzipBytes": 7762,
|
"gzipBytes": 11206,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/controller-observability/v1.html",
|
"uri": "ui://shd/controller-observability/v1.html",
|
||||||
"sha256": "de6f1c6e07d74bc58555f41a22e9a0e8a3942577ffce5773f9450707cb7ca440"
|
"sha256": "74cc8cd974ddfb0563b213166398cf37e98cd477e010c9fbcc444324b5366a5c"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
e431183d3b959601687632ea36b207fc88c85f88c69c2d3abdd342be411db50c index.html
|
2704ffa6778cef26875f187623c2e58e3d0a76085b5369838b9abc7da137a789 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/crm-funnel/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/crm-funnel/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only CRM deal funnel grouped by stage.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77269,
|
"bytes": 78910,
|
||||||
"gzipBytes": 20604,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/crm-funnel/v1.html",
|
"uri": "ui://shd/crm-funnel/v1.html",
|
||||||
"sha256": "e431183d3b959601687632ea36b207fc88c85f88c69c2d3abdd342be411db50c"
|
"sha256": "2704ffa6778cef26875f187623c2e58e3d0a76085b5369838b9abc7da137a789"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/crm-funnel/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
6fde9e254a5ff34e4cc1110f729b35661bc08ece41cae8d7c9783549409a3d9c index.html
|
f859c752e2ae3eacdc7a4fb18ec460cbfddb62a327a7d636b76b5482ec4ad7fd index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# CRM widget
|
# CRM widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/crm/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/crm/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -7,14 +7,17 @@
|
|||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"uri": "ui://shd/crm/v1.html",
|
"uri": "ui://shd/crm/v1.html",
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"bytes": 46636,
|
"bytes": 78896,
|
||||||
"sha256": "6fde9e254a5ff34e4cc1110f729b35661bc08ece41cae8d7c9783549409a3d9c",
|
"sha256": "f859c752e2ae3eacdc7a4fb18ec460cbfddb62a327a7d636b76b5482ec4ad7fd",
|
||||||
"gzipBytes": 12425
|
"gzipBytes": 21186
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/crm/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
d051f38e8d81151a3269d16effae3bca502527680a106c7f9b9d7942b9f7446c index.html
|
273dd8aaf6ef1e9ead2312484a7a7d08bfe3b0258cb0e9eb71f9061a4b777922 index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Discussions widget
|
# Discussions widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/discussions/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/discussions/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides topics and read-only message details.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 49141,
|
"bytes": 78912,
|
||||||
"gzipBytes": 14385,
|
"gzipBytes": 21185,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/discussions/v1.html",
|
"uri": "ui://shd/discussions/v1.html",
|
||||||
"sha256": "d051f38e8d81151a3269d16effae3bca502527680a106c7f9b9d7942b9f7446c"
|
"sha256": "273dd8aaf6ef1e9ead2312484a7a7d08bfe3b0258cb0e9eb71f9061a4b777922"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/discussions/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
6964112ce1ae2ec25a736709bc00ad0a7335177ec98547ad48d672834e491760 index.html
|
c99dc13e4cf8302699f9ae8cdf84462b999e20eee49b2baf18b620fb083f9e6f index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/documents-completeness/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/documents-completeness/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Terms and document completeness dashboard.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77329,
|
"bytes": 78934,
|
||||||
"gzipBytes": 20618,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/documents-completeness/v1.html",
|
"uri": "ui://shd/documents-completeness/v1.html",
|
||||||
"sha256": "6964112ce1ae2ec25a736709bc00ad0a7335177ec98547ad48d672834e491760"
|
"sha256": "c99dc13e4cf8302699f9ae8cdf84462b999e20eee49b2baf18b620fb083f9e6f"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/documents-completeness/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
bc1b879e77c078049a8dcbf237792eb00c2975ad7b419c8b12aea0f60930a3a8 index.html
|
761ab899858efa595426ed1eda8ccac6315ffec5bad524503f74bf0c1853a20f index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Документы widget
|
# Документы widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/documents/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/documents/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -7,14 +7,17 @@
|
|||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"uri": "ui://shd/documents/v1.html",
|
"uri": "ui://shd/documents/v1.html",
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"bytes": 46686,
|
"bytes": 78908,
|
||||||
"sha256": "bc1b879e77c078049a8dcbf237792eb00c2975ad7b419c8b12aea0f60930a3a8",
|
"sha256": "761ab899858efa595426ed1eda8ccac6315ffec5bad524503f74bf0c1853a20f",
|
||||||
"gzipBytes": 12419
|
"gzipBytes": 21188
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/documents/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
e410bd6143c07e677ef23ede0be1d63e17587bacaf16732334701b4a43399e5d index.html
|
5ca14633d758f98ecbfeb3b20cee1a09140dbaf724e1e12e28bf5ce0c89bd3de index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/files/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/files/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Project file register with safe read-only links.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 53667,
|
"bytes": 78900,
|
||||||
"gzipBytes": 15488,
|
"gzipBytes": 21187,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/files/v1.html",
|
"uri": "ui://shd/files/v1.html",
|
||||||
"sha256": "e410bd6143c07e677ef23ede0be1d63e17587bacaf16732334701b4a43399e5d"
|
"sha256": "5ca14633d758f98ecbfeb3b20cee1a09140dbaf724e1e12e28bf5ce0c89bd3de"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/files/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
20231dba4f8d99b89d33bfe8a9247d3d8728d17fd75bf07cd10ed9950ad0913c index.html
|
57681855fe46c8bd436a53b851f1c6ee646014b65ff93913af40f0d0f118e85c index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/finance-dashboard/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/finance-dashboard/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only finance totals, statuses and records.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77283,
|
"bytes": 78924,
|
||||||
"gzipBytes": 20601,
|
"gzipBytes": 21187,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/finance-dashboard/v1.html",
|
"uri": "ui://shd/finance-dashboard/v1.html",
|
||||||
"sha256": "20231dba4f8d99b89d33bfe8a9247d3d8728d17fd75bf07cd10ed9950ad0913c"
|
"sha256": "57681855fe46c8bd436a53b851f1c6ee646014b65ff93913af40f0d0f118e85c"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/finance-dashboard/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
9602680407f0da06e87d71ad3374dbd4602d6ca6124f19983647b207c33c5b6e index.html
|
7b1a5c129a4995ca3b8850568d9eec5c1fb949a132efbfcd6658fc94a03747d3 index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Финансы widget
|
# Финансы widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/finance/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/finance/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -7,14 +7,17 @@
|
|||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"uri": "ui://shd/finance/v1.html",
|
"uri": "ui://shd/finance/v1.html",
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"bytes": 46724,
|
"bytes": 78904,
|
||||||
"sha256": "9602680407f0da06e87d71ad3374dbd4602d6ca6124f19983647b207c33c5b6e",
|
"sha256": "7b1a5c129a4995ca3b8850568d9eec5c1fb949a132efbfcd6658fc94a03747d3",
|
||||||
"gzipBytes": 12444
|
"gzipBytes": 21188
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/finance/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
c761b817f26067ed2697d6c5da3f99a0e962dbf920a1c13ff24165dde910af7c index.html
|
6e92d2e27b29d3b90c4ca66c23891fd32fcb07e3a467f40009bd5dba916a9fc0 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/gitea-board/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/gitea-board/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Gitea issues grouped by state.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77271,
|
"bytes": 78912,
|
||||||
"gzipBytes": 20602,
|
"gzipBytes": 21188,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/gitea-board/v1.html",
|
"uri": "ui://shd/gitea-board/v1.html",
|
||||||
"sha256": "c761b817f26067ed2697d6c5da3f99a0e962dbf920a1c13ff24165dde910af7c"
|
"sha256": "6e92d2e27b29d3b90c4ca66c23891fd32fcb07e3a467f40009bd5dba916a9fc0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/gitea-board/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
dac317642f83f1a2599fff7041e340f4fdc5620733baaaa426a69edf5d9c7442 index.html
|
da4af974cf16b4f442d70ecaa6e634b76eb0ff63817a401c3a03fc65f86b1ad6 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/gitea/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/gitea/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Read-only issue register for allowed targets.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 53667,
|
"bytes": 78900,
|
||||||
"gzipBytes": 15487,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/gitea/v1.html",
|
"uri": "ui://shd/gitea/v1.html",
|
||||||
"sha256": "dac317642f83f1a2599fff7041e340f4fdc5620733baaaa426a69edf5d9c7442"
|
"sha256": "da4af974cf16b4f442d70ecaa6e634b76eb0ff63817a401c3a03fc65f86b1ad6"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/gitea/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ea7d3bc74188e3adc144c07404cd6a26779b42d4cc3fb6ffc83f113fb64288bc index.html
|
bfcc6032e183d611579240950bd97bc7747222f55cb4ccf01905ead46b0bf9bc index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/inventory-warnings/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/inventory-warnings/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only low-stock and critical inventory dashboard.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77285,
|
"bytes": 78926,
|
||||||
"gzipBytes": 20606,
|
"gzipBytes": 21188,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/inventory-warnings/v1.html",
|
"uri": "ui://shd/inventory-warnings/v1.html",
|
||||||
"sha256": "ea7d3bc74188e3adc144c07404cd6a26779b42d4cc3fb6ffc83f113fb64288bc"
|
"sha256": "bfcc6032e183d611579240950bd97bc7747222f55cb4ccf01905ead46b0bf9bc"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/inventory-warnings/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
fda7797f99a07415ecc4879f44a04c936953c055f79bdee2e1f3e88513fdff7d index.html
|
26bd7d21dd1613d99919bc0048a697e00ce2f302be194d8783ba43d9fced2023 index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Inventory widget
|
# Inventory widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/inventory/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/inventory/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides stock quantities and read-only item details.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 49137,
|
"bytes": 78908,
|
||||||
"gzipBytes": 14388,
|
"gzipBytes": 21187,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/inventory/v1.html",
|
"uri": "ui://shd/inventory/v1.html",
|
||||||
"sha256": "fda7797f99a07415ecc4879f44a04c936953c055f79bdee2e1f3e88513fdff7d"
|
"sha256": "26bd7d21dd1613d99919bc0048a697e00ce2f302be194d8783ba43d9fced2023"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/inventory/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
ca76aa4e10fe1a5f2d167fe8876a3d009aca9a2e386cde5f8743ede370272221 index.html
|
b03ee937b1537989b9ca852ed17b9033e34968010947b4cadfc78bcb5781d1a3 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/notes-tree/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/notes-tree/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides a read-only Notes spaces and hierarchy tree.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 77269,
|
"bytes": 78910,
|
||||||
"gzipBytes": 20607,
|
"gzipBytes": 21189,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/notes-tree/v1.html",
|
"uri": "ui://shd/notes-tree/v1.html",
|
||||||
"sha256": "ca76aa4e10fe1a5f2d167fe8876a3d009aca9a2e386cde5f8743ede370272221"
|
"sha256": "b03ee937b1537989b9ca852ed17b9033e34968010947b4cadfc78bcb5781d1a3"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/notes-tree/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
cb4881dca8cf3f999b27a540239f9a4d17204bc4ba5e65a4bf2f5ed0e7810e23 index.html
|
a2114b5a6014c9d266b7277e29270a916765c6d6b4b7c5625610762b46b67eae index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Notes widget
|
# Notes widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/notes/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/notes/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides page register and read-only page details.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 49129,
|
"bytes": 78900,
|
||||||
"gzipBytes": 14384,
|
"gzipBytes": 21186,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/notes/v1.html",
|
"uri": "ui://shd/notes/v1.html",
|
||||||
"sha256": "cb4881dca8cf3f999b27a540239f9a4d17204bc4ba5e65a4bf2f5ed0e7810e23"
|
"sha256": "a2114b5a6014c9d266b7277e29270a916765c6d6b4b7c5625610762b46b67eae"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/notes/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
c19707e63a55e05ee4000f6a3e0b4ea9e5955623899ed21213e2a6cb4e6d3c74 index.html
|
e7f8ffe04b2c043e46930d560653870c77725f5748585514fff6266c4dfd7584 index.html
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Notifications widget
|
# Notifications widget
|
||||||
|
|
||||||
This artifact is served by the SHD MCP backend as `ui://shd/notifications/v1.html`.
|
This artifact is served by the SHD MCP backend as ui://shd/notifications/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps `window.openai` as a compatibility extension. It has no external script or network dependency. The widget provides notifications and read-only event details.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -5,16 +5,19 @@
|
|||||||
"resources": [
|
"resources": [
|
||||||
{
|
{
|
||||||
"file": "index.html",
|
"file": "index.html",
|
||||||
"bytes": 49145,
|
"bytes": 78916,
|
||||||
"gzipBytes": 14386,
|
"gzipBytes": 21183,
|
||||||
"mediaType": "text/html;profile=mcp-app",
|
"mediaType": "text/html;profile=mcp-app",
|
||||||
"uri": "ui://shd/notifications/v1.html",
|
"uri": "ui://shd/notifications/v1.html",
|
||||||
"sha256": "c19707e63a55e05ee4000f6a3e0b4ea9e5955623899ed21213e2a6cb4e6d3c74"
|
"sha256": "e7f8ffe04b2c043e46930d560653870c77725f5748585514fff6266c4dfd7584"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "../../../../../LICENSE",
|
"license": "../../../../../LICENSE",
|
||||||
"source": {
|
"source": {
|
||||||
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
"repository": "https://github.com/bulava92/shd-mcp-plugin",
|
||||||
"path": "plugins/shd-mcp-plugin/widgets/notifications/v1/index.html"
|
"path": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget.html",
|
||||||
|
"generatedBy": "scripts/generate-widget-artifacts.mjs",
|
||||||
|
"engine": "shd-universal-v1",
|
||||||
|
"config": "plugins/shd-mcp-plugin/widgets/_shared/universal-widget-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
333f9d1635a1bba89ae8adf4af8c29b011df20cd781e434a42f9c58e413e8999 index.html
|
1b4f9c6696b76cd79064fa5de6b3a43892be21c7b3cb9e635e9ce56cc9b90055 index.html
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
This artifact is served by the SHD MCP backend as ui://shd/organizations/v1.html.
|
This artifact is served by the SHD MCP backend as ui://shd/organizations/v1.html.
|
||||||
|
|
||||||
The HTML is standalone, uses the MCP Apps bridge first, and keeps window.openai as a compatibility extension. It has no external script or network dependency. The widget provides Organization register with safe membership details.
|
The standalone HTML is generated from `widgets/_shared/universal-widget.html` and `widgets/_shared/universal-widget-config.json` by `scripts/generate-widget-artifacts.mjs` using the `shd-universal-v1` engine. It uses the MCP Apps bridge first and keeps `window.openai` as a compatibility extension; it has no external script or network dependency. The module supplies its data and view configuration through the render-tool metadata.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user