Files
UNITRONIX dd0889e3d6 Add SessionManager; update docs, i18n & CI
Introduce a SessionManager for relay-based remote sessions in the Tauri MGMT client: new SessionCommand API, start/stop/session input routing, clipboard/recording/quality controls, and notification read/dismiss state. Wire AppState with new mutexes and show main window on startup. CI: add SBOM generation (anchore) and Trivy vulnerability scan steps. Misc: change console Docker DB path, large README/CHANGELOG updates (chat E2E, unattended access/WOL, i18n expansion, CDAP/SDK docs), and many web-nodejs assets/locales/routes/views/services and server-side changes.
2026-04-05 13:42:07 +02:00

276 lines
14 KiB
HTML

<%- include('layouts/studio', {
title: _('sdk_studio.title'),
pageStyles: ['cdap-studio'],
pageScripts: ['cdap-studio'],
currentPage: 'cdap-studio',
body: `
<div class="studio-container" id="studio-container">
<!-- ── Toolbar ───────────────────────────────────────────── -->
<div class="studio-toolbar" id="studio-toolbar">
<div class="studio-toolbar-left">
<a href="/" class="studio-tool-btn studio-back-btn" title="${_('common.back') || 'Back'}">
<span class="material-icons">arrow_back</span>
</a>
<div class="studio-toolbar-sep"></div>
<button class="studio-tool-btn" id="btn-new-flow" title="${_('sdk_studio.new_flow')}">
<span class="material-icons">add</span>
<span class="studio-tool-label">${_('sdk_studio.new_flow')}</span>
</button>
<button class="studio-tool-btn" id="btn-open-flow" title="${_('sdk_studio.open_flow')}">
<span class="material-icons">folder_open</span>
<span class="studio-tool-label">${_('sdk_studio.open_flow')}</span>
</button>
<button class="studio-tool-btn" id="btn-save-flow" title="${_('sdk_studio.save_flow')}">
<span class="material-icons">save</span>
<span class="studio-tool-label">${_('sdk_studio.save_flow')}</span>
</button>
<button class="studio-tool-btn" id="btn-import-flow" title="${_('sdk_studio.import_flow')}">
<span class="material-icons">upload_file</span>
</button>
<input type="file" id="import-flow-input" accept=".bdflow,.json" style="display:none">
<div class="studio-toolbar-sep"></div>
<button class="studio-tool-btn studio-tool-accent" id="btn-run-flow" title="${_('sdk_studio.run_flow')}">
<span class="material-icons">play_arrow</span>
<span class="studio-tool-label">${_('sdk_studio.run_flow')}</span>
</button>
<button class="studio-tool-btn" id="btn-debug-flow" title="${_('sdk_studio.debug')}">
<span class="material-icons">bug_report</span>
<span class="studio-tool-label">${_('sdk_studio.debug')}</span>
</button>
<button class="studio-tool-btn" id="btn-deploy-flow" title="${_('sdk_studio.deploy')}">
<span class="material-icons">rocket_launch</span>
<span class="studio-tool-label">${_('sdk_studio.deploy')}</span>
</button>
<div class="studio-toolbar-sep"></div>
<button class="studio-tool-btn" id="btn-undo" title="${_('sdk_studio.undo')}">
<span class="material-icons">undo</span>
</button>
<button class="studio-tool-btn" id="btn-redo" title="${_('sdk_studio.redo')}">
<span class="material-icons">redo</span>
</button>
</div>
<div class="studio-toolbar-right">
<span class="studio-flow-name" id="studio-flow-name">${_('sdk_studio.untitled')}</span>
<div class="studio-toolbar-sep"></div>
<button class="studio-tool-btn" id="btn-toggle-code" title="${_('sdk_studio.code_mode')}">
<span class="material-icons">code</span>
</button>
<button class="studio-tool-btn" id="btn-toggle-grid" title="${_('sdk_studio.toggle_grid')}">
<span class="material-icons">grid_on</span>
</button>
<div class="studio-zoom-group">
<button class="studio-tool-btn studio-tool-sm" id="btn-zoom-out">
<span class="material-icons">remove</span>
</button>
<span class="studio-zoom-label" id="studio-zoom-label">100%</span>
<button class="studio-tool-btn studio-tool-sm" id="btn-zoom-in">
<span class="material-icons">add</span>
</button>
<button class="studio-tool-btn studio-tool-sm" id="btn-zoom-fit" title="${_('sdk_studio.zoom_fit')}">
<span class="material-icons">fit_screen</span>
</button>
</div>
</div>
</div>
<!-- ── Main Workspace ────────────────────────────────────── -->
<div class="studio-workspace">
<!-- Palette panel (left) -->
<div class="studio-palette" id="studio-palette">
<div class="studio-panel-header">
<span class="material-icons">widgets</span>
<span>${_('sdk_studio.palette')}</span>
</div>
<input type="text" class="studio-palette-search" id="palette-search"
placeholder="${_('sdk_studio.search_nodes')}" autocomplete="off">
<div class="studio-palette-category" data-category="sources">
<button class="studio-palette-cat-btn" data-cat="sources">
<span class="material-icons">sensors</span>
<span>${_('sdk_studio.cat_sources')}</span>
<span class="material-icons studio-cat-chevron">expand_more</span>
</button>
<div class="studio-palette-items" id="palette-sources"></div>
</div>
<div class="studio-palette-category" data-category="processing">
<button class="studio-palette-cat-btn" data-cat="processing">
<span class="material-icons">settings_suggest</span>
<span>${_('sdk_studio.cat_processing')}</span>
<span class="material-icons studio-cat-chevron">expand_more</span>
</button>
<div class="studio-palette-items" id="palette-processing"></div>
</div>
<div class="studio-palette-category" data-category="outputs">
<button class="studio-palette-cat-btn" data-cat="outputs">
<span class="material-icons">output</span>
<span>${_('sdk_studio.cat_outputs')}</span>
<span class="material-icons studio-cat-chevron">expand_more</span>
</button>
<div class="studio-palette-items" id="palette-outputs"></div>
</div>
</div>
<!-- Canvas (center) -->
<div class="studio-canvas-wrap" id="studio-canvas-wrap">
<svg class="studio-canvas-svg" id="studio-canvas-svg">
<defs>
<pattern id="studio-grid" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.5" fill="var(--studio-grid-color)" />
</pattern>
<marker id="arrow-data" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--studio-wire-data)"/>
</marker>
<marker id="arrow-error" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="var(--studio-wire-error)"/>
</marker>
</defs>
<rect width="100%" height="100%" fill="url(#studio-grid)" class="studio-grid-rect" id="studio-grid-rect"/>
<g id="studio-wires-layer"></g>
<g id="studio-nodes-layer"></g>
<line id="studio-temp-wire" class="studio-temp-wire" x1="0" y1="0" x2="0" y2="0" style="display:none"/>
</svg>
<!-- Selection box -->
<div class="studio-select-box" id="studio-select-box" style="display:none"></div>
<!-- Minimap -->
<div class="studio-minimap" id="studio-minimap">
<canvas id="studio-minimap-canvas" width="160" height="100"></canvas>
<div class="studio-minimap-viewport" id="studio-minimap-viewport"></div>
</div>
<!-- Empty state overlay -->
<div class="studio-empty-state" id="studio-empty-state">
<span class="material-icons">account_tree</span>
<h3>${_('sdk_studio.empty_title')}</h3>
<p>${_('sdk_studio.empty_desc')}</p>
<div class="studio-empty-actions">
<button class="btn btn-primary" id="btn-empty-template">
<span class="material-icons">auto_awesome</span>
${_('sdk_studio.browse_templates')}
</button>
</div>
</div>
</div>
<!-- Right panel: Inspector + Console -->
<div class="studio-right-panel" id="studio-right-panel">
<!-- Inspector -->
<div class="studio-inspector" id="studio-inspector">
<div class="studio-panel-header">
<span class="material-icons">tune</span>
<span>${_('sdk_studio.inspector')}</span>
</div>
<div class="studio-inspector-body" id="studio-inspector-body">
<div class="studio-inspector-empty">
<span class="material-icons">touch_app</span>
<p>${_('sdk_studio.select_node')}</p>
</div>
</div>
</div>
<!-- Console -->
<div class="studio-console" id="studio-console">
<div class="studio-panel-header studio-console-header">
<span class="material-icons">terminal</span>
<span>${_('sdk_studio.console')}</span>
<button class="studio-console-clear" id="btn-clear-console" title="${_('sdk_studio.clear_console')}">
<span class="material-icons">delete_sweep</span>
</button>
</div>
<div class="studio-console-body" id="studio-console-body">
<div class="studio-console-line studio-console-info">
<span class="studio-console-time"></span>
<span class="studio-console-msg">${_('sdk_studio.console_ready')}</span>
</div>
</div>
</div>
</div>
</div>
<!-- ── Status Bar ────────────────────────────────────────── -->
<div class="studio-statusbar" id="studio-statusbar">
<span class="studio-status-item" id="status-flow-name">
<span class="material-icons">description</span>
<span id="status-flow-label">${_('sdk_studio.untitled')}</span>
</span>
<span class="studio-status-item" id="status-node-count">
<span class="material-icons">category</span>
<span>${_('sdk_studio.nodes')}: <b id="status-nodes">0</b></span>
</span>
<span class="studio-status-item" id="status-wire-count">
<span class="material-icons">timeline</span>
<span>${_('sdk_studio.wires')}: <b id="status-wires">0</b></span>
</span>
<span class="studio-status-sep"></span>
<span class="studio-status-item studio-status-right" id="status-state">
<span class="studio-status-dot studio-status-ready"></span>
<span id="status-state-text">${_('sdk_studio.status_ready')}</span>
</span>
</div>
</div>
<!-- ── Modals ─────────────────────────────────────────────────── -->
<!-- Open Flow modal -->
<div class="modal-overlay" id="modal-open-flow" style="display:none">
<div class="modal modal-lg">
<div class="modal-header">
<h3>${_('sdk_studio.open_flow')}</h3>
<button class="modal-close" data-close="modal-open-flow">&times;</button>
</div>
<div class="modal-body">
<div class="studio-flow-list" id="flow-list-container">
<p class="text-muted">${_('sdk_studio.loading')}</p>
</div>
</div>
</div>
</div>
<!-- Templates modal -->
<div class="modal-overlay" id="modal-templates" style="display:none">
<div class="modal modal-lg">
<div class="modal-header">
<h3>${_('sdk_studio.browse_templates')}</h3>
<button class="modal-close" data-close="modal-templates">&times;</button>
</div>
<div class="modal-body">
<div class="studio-template-grid" id="template-grid-container">
<p class="text-muted">${_('sdk_studio.loading')}</p>
</div>
</div>
</div>
</div>
<!-- Save Flow modal -->
<div class="modal-overlay" id="modal-save-flow" style="display:none">
<div class="modal">
<div class="modal-header">
<h3>${_('sdk_studio.save_flow')}</h3>
<button class="modal-close" data-close="modal-save-flow">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>${_('sdk_studio.flow_name')}</label>
<input type="text" id="save-flow-name" class="form-input" maxlength="100" autocomplete="off">
</div>
<div class="form-group">
<label>${_('sdk_studio.flow_desc')}</label>
<textarea id="save-flow-desc" class="form-textarea" rows="3" maxlength="500"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-close="modal-save-flow">${_('common.cancel')}</button>
<button class="btn btn-primary" id="btn-save-confirm">${_('sdk_studio.save_flow')}</button>
</div>
</div>
</div>
`
}) %>