mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 09:35:39 +00:00
dd0889e3d6
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.
276 lines
14 KiB
HTML
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">×</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">×</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">×</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>
|
|
|
|
`
|
|
}) %>
|