Refine routing UI and rebuild Windows package
This commit is contained in:
Vendored
BIN
Binary file not shown.
Vendored
+6
-4
File diff suppressed because one or more lines are too long
+246155
-57316
File diff suppressed because it is too large
Load Diff
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Generated
+1133
-1623
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -9,11 +9,11 @@
|
||||
"dist:win": "set CSC_IDENTITY_AUTO_DISCOVERY=false&& electron-builder --win portable"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^31.7.7",
|
||||
"electron-builder": "^24.13.3"
|
||||
"electron": "^42.4.0",
|
||||
"electron-builder": "^26.15.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"knx": "^2.5.4",
|
||||
"knx": "^0.0.1",
|
||||
"modbus-serial": "^8.0.25",
|
||||
"mqtt": "^5.10.4",
|
||||
"serialport": "^13.0.0"
|
||||
|
||||
+79
-27
@@ -3,7 +3,7 @@
|
||||
const RULER_HEIGHT = 34;
|
||||
const POINT_RADIUS = 7;
|
||||
const FPS_DEFAULT = 30;
|
||||
const LEFT_GUTTER = 126;
|
||||
const LEFT_GUTTER = 162;
|
||||
const RIGHT_PADDING = 24;
|
||||
const MIN_CANVAS_HEIGHT = 420;
|
||||
const EXTERNAL_RESYNC_MS = 1000;
|
||||
@@ -189,7 +189,7 @@
|
||||
routing: createDefaultRouting(),
|
||||
ui: {
|
||||
settingsOpen: false,
|
||||
settingsTab: "routing"
|
||||
settingsTab: "midi"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -531,13 +531,13 @@
|
||||
if (track.type === "sacn-channel") return `Uni ${track.universe} / Addr ${track.address}`;
|
||||
if (track.type === "audio-file") {
|
||||
const bpmText = track.analysisBpm ? ` / ${Math.round(track.analysisBpm)} BPM` : "";
|
||||
return `${escapeHtml(track.mediaName || basenameFromPath(track.filePath))}${bpmText}`;
|
||||
return `${track.mediaName || basenameFromPath(track.filePath)}${bpmText}`;
|
||||
}
|
||||
if (track.type === "serial-value") return escapeHtml(track.template);
|
||||
if (track.type === "serial-value") return track.template;
|
||||
if (track.type === "modbus-tcp" || track.type === "modbus-rtu") return `ID ${track.unitId} / Addr ${track.address}`;
|
||||
if (track.type === "knx-value") return `${escapeHtml(track.groupAddress)} / ${escapeHtml(track.dpt)}`;
|
||||
if (track.type === "osc-value") return escapeHtml(track.address);
|
||||
return escapeHtml(track.topic);
|
||||
if (track.type === "knx-value") return `${track.groupAddress} / ${track.dpt}`;
|
||||
if (track.type === "osc-value") return track.address;
|
||||
return track.topic;
|
||||
}
|
||||
|
||||
function basenameFromPath(filePath) {
|
||||
@@ -829,6 +829,29 @@
|
||||
return escapeHtml(value).replaceAll("\"", """);
|
||||
}
|
||||
|
||||
function shortenLabel(value, maxLength = 18) {
|
||||
const text = String(value || "").trim();
|
||||
if (text.length <= maxLength) {
|
||||
return text;
|
||||
}
|
||||
return `${text.slice(0, Math.max(1, maxLength - 3)).trimEnd()}...`;
|
||||
}
|
||||
|
||||
function truncateCanvasText(text, maxWidth) {
|
||||
const value = String(text || "");
|
||||
if (!value) {
|
||||
return "";
|
||||
}
|
||||
if (ctx.measureText(value).width <= maxWidth) {
|
||||
return value;
|
||||
}
|
||||
let shortened = value;
|
||||
while (shortened.length > 1 && ctx.measureText(`${shortened}...`).width > maxWidth) {
|
||||
shortened = shortened.slice(0, -1);
|
||||
}
|
||||
return `${shortened.trimEnd()}...`;
|
||||
}
|
||||
|
||||
function chooseTimeStep(duration) {
|
||||
if (duration <= 15) return 0.5;
|
||||
if (duration <= 60) return 1;
|
||||
@@ -921,25 +944,29 @@
|
||||
function drawTrackLaneLabel(track, trackIndex) {
|
||||
const top = RULER_HEIGHT + (trackIndex * TRACK_HEIGHT);
|
||||
const color = getTrackColor(track);
|
||||
const labelLeft = 66;
|
||||
const labelWidth = LEFT_GUTTER - labelLeft - 14;
|
||||
const trackName = track.name || "Unbenannt";
|
||||
const detailText = describeTrack(track);
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(18, top + 14, 44, 24, 8);
|
||||
ctx.roundRect(16, top + 15, 40, 22, 8);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = "#091018";
|
||||
ctx.font = "bold 11px Trebuchet MS";
|
||||
ctx.font = "bold 10px Trebuchet MS";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(getTrackKindLabel(track), 40, top + 30);
|
||||
ctx.fillText(getTrackKindLabel(track), 36, top + 29);
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(92, top + (TRACK_HEIGHT / 2));
|
||||
ctx.rotate(-Math.PI / 2);
|
||||
ctx.textAlign = "left";
|
||||
ctx.fillStyle = "rgba(255,255,255,0.86)";
|
||||
ctx.font = "bold 13px Trebuchet MS";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(track.name, 0, 0);
|
||||
ctx.restore();
|
||||
ctx.font = "bold 12px Trebuchet MS";
|
||||
ctx.fillText(truncateCanvasText(trackName, labelWidth), labelLeft, top + 33);
|
||||
|
||||
ctx.fillStyle = "rgba(164,178,204,0.92)";
|
||||
ctx.font = "11px Trebuchet MS";
|
||||
ctx.fillText(truncateCanvasText(detailText, labelWidth), labelLeft, top + 52);
|
||||
|
||||
if (track.id === state.selectedTrackId) {
|
||||
ctx.strokeStyle = "rgba(110,194,242,0.76)";
|
||||
@@ -1137,12 +1164,13 @@
|
||||
function renderTimelineList() {
|
||||
refs.timelineList.innerHTML = "";
|
||||
for (const timeline of state.project.timelines) {
|
||||
const shortName = shortenLabel(timeline.name, 26);
|
||||
const card = document.createElement("div");
|
||||
card.className = `timeline-card selectable-card ${timeline.id === state.selectedTimelineId ? "active" : ""}`;
|
||||
card.dataset.timelineId = timeline.id;
|
||||
card.innerHTML = `
|
||||
<strong>${escapeHtml(timeline.name)}</strong>
|
||||
<div class="meta-row">
|
||||
<strong class="truncate-text" title="${escapeAttribute(timeline.name)}">${escapeHtml(shortName)}</strong>
|
||||
<div class="meta-row compact">
|
||||
<span>${timeline.tracks.length} Tracks</span>
|
||||
<span>${timeline.duration}s</span>
|
||||
</div>
|
||||
@@ -1173,18 +1201,22 @@
|
||||
}
|
||||
|
||||
for (const track of timeline.tracks) {
|
||||
const fullName = track.name || "Unbenannt";
|
||||
const shortName = shortenLabel(fullName, 24);
|
||||
const detail = describeTrack(track);
|
||||
const shortDetail = shortenLabel(detail, 34);
|
||||
const card = document.createElement("div");
|
||||
card.className = `track-card ${track.id === state.selectedTrackId ? "active" : ""}`;
|
||||
card.dataset.trackId = track.id;
|
||||
card.innerHTML = `
|
||||
<div class="track-card-top">
|
||||
<div class="track-card-body selectable-card" data-track-id="${track.id}">
|
||||
<div class="panel-header">
|
||||
<strong>${escapeHtml(track.name)}</strong>
|
||||
<span class="track-pill ${pillClassForTrack(track)}">${getTrackKindLabel(track)}</span>
|
||||
<div class="track-card-header">
|
||||
<strong class="truncate-text" title="${escapeAttribute(fullName)}">${escapeHtml(shortName)}</strong>
|
||||
<span class="track-pill ${pillClassForTrack(track)}" title="${escapeAttribute(getTrackKindLabel(track))}">${getTrackKindLabel(track)}</span>
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
<span>${describeTrack(track)}</span>
|
||||
<div class="meta-row compact">
|
||||
<span class="truncate-text" title="${escapeAttribute(detail)}">${escapeHtml(shortDetail)}</span>
|
||||
<span>${track.points.length} Punkte</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1201,6 +1233,7 @@
|
||||
function renderTimelineInspector() {
|
||||
const timeline = getSelectedTimeline();
|
||||
refs.timelineTitle.textContent = timeline ? timeline.name : "Keine Timeline ausgewaehlt";
|
||||
refs.timelineTitle.title = timeline ? timeline.name : "";
|
||||
if (!timeline) {
|
||||
refs.timelineInspector.innerHTML = `<div class="empty-state">Keine Timeline ausgewaehlt.</div>`;
|
||||
return;
|
||||
@@ -1635,12 +1668,22 @@
|
||||
|
||||
function openSettings(tab = state.ui.settingsTab) {
|
||||
state.ui.settingsOpen = true;
|
||||
state.ui.settingsTab = tab;
|
||||
state.ui.settingsTab = tab || "midi";
|
||||
void refreshNetworkAdapters();
|
||||
void refreshAudioOutputs();
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function updateCanvasHoverTitle(x, y) {
|
||||
const hit = findCanvasHit(x, y);
|
||||
const nextTitle = hit?.track
|
||||
? `${hit.track.name || "Unbenannt"}\n${describeTrack(hit.track)}`
|
||||
: "";
|
||||
if (refs.timelineCanvas.title !== nextTitle) {
|
||||
refs.timelineCanvas.title = nextTitle;
|
||||
}
|
||||
}
|
||||
|
||||
function closeSettings() {
|
||||
state.ui.settingsOpen = false;
|
||||
renderAll();
|
||||
@@ -2896,12 +2939,12 @@
|
||||
refs.playButton.addEventListener("click", playTransport);
|
||||
refs.pauseButton.addEventListener("click", pauseTransport);
|
||||
refs.stopButton.addEventListener("click", () => stopTransport(true));
|
||||
refs.openSettingsButton.addEventListener("click", () => openSettings("routing"));
|
||||
refs.openSettingsButton.addEventListener("click", () => openSettings());
|
||||
refs.closeSettingsButton.addEventListener("click", closeSettings);
|
||||
refs.settingsBackdrop.addEventListener("click", closeSettings);
|
||||
refs.settingsTabButtons.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
state.ui.settingsTab = button.dataset.settingsTab || "routing";
|
||||
state.ui.settingsTab = button.dataset.settingsTab || "midi";
|
||||
renderAll();
|
||||
});
|
||||
});
|
||||
@@ -2998,6 +3041,15 @@
|
||||
addPointFromCanvas(event.clientX - rect.left, event.clientY - rect.top);
|
||||
});
|
||||
|
||||
refs.timelineCanvas.addEventListener("mousemove", (event) => {
|
||||
const rect = refs.timelineCanvas.getBoundingClientRect();
|
||||
updateCanvasHoverTitle(event.clientX - rect.left, event.clientY - rect.top);
|
||||
});
|
||||
|
||||
refs.timelineCanvas.addEventListener("mouseleave", () => {
|
||||
refs.timelineCanvas.title = "";
|
||||
});
|
||||
|
||||
refs.timelineCanvas.addEventListener("mousedown", (event) => {
|
||||
const rect = refs.timelineCanvas.getBoundingClientRect();
|
||||
const x = event.clientX - rect.left;
|
||||
|
||||
+288
-267
@@ -97,14 +97,14 @@
|
||||
|
||||
<section class="editor panel">
|
||||
<div class="editor-header">
|
||||
<div>
|
||||
<div class="editor-title-block">
|
||||
<div class="eyebrow">Editor</div>
|
||||
<h2 id="timelineTitle">Keine Timeline ausgewählt</h2>
|
||||
<h2 id="timelineTitle">Keine Timeline ausgewaehlt</h2>
|
||||
</div>
|
||||
<div class="editor-hints">
|
||||
<span>Doppelklick: Punkt setzen</span>
|
||||
<span>Ziehen: Zeit/Wert ändern</span>
|
||||
<span>Entf: Punkt löschen</span>
|
||||
<span>Ziehen: Zeit/Wert aendern</span>
|
||||
<span>Entf: Punkt loeschen</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="canvas-frame">
|
||||
@@ -112,6 +112,22 @@
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<aside class="inspector">
|
||||
<section class="panel inspector-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Timeline</h2>
|
||||
</div>
|
||||
<div id="timelineInspector"></div>
|
||||
</section>
|
||||
|
||||
<section class="panel inspector-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Auswahl</h2>
|
||||
</div>
|
||||
<div id="selectionInspector"></div>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<div id="settingsModal" class="settings-modal" aria-hidden="true">
|
||||
@@ -127,311 +143,316 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-tabbar" role="tablist" aria-label="Einstellungen">
|
||||
<button class="settings-tab active" type="button" data-settings-tab="routing">Routing</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="timeline">Timeline</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="selection">Auswahl</button>
|
||||
<div class="settings-tabbar" role="tablist" aria-label="Ausgaben">
|
||||
<button class="settings-tab active" type="button" data-settings-tab="midi" title="MIDI">MIDI</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="audio" title="Audio">Audio</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="artnet" title="Art-Net">Art-Net</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="sacn" title="sACN">sACN</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="osc" title="Open Sound Control">OSC</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="mqtt" title="MQTT">MQTT</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="serial" title="Serial">Serial</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="modbusTcp" title="Modbus TCP">MB TCP</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="modbusRtu" title="Modbus RTU">MB RTU</button>
|
||||
<button class="settings-tab" type="button" data-settings-tab="knx" title="KNX">KNX</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-content">
|
||||
<div class="settings-tab-panel active" data-settings-panel="routing">
|
||||
<div class="settings-stack">
|
||||
<div class="info-card">
|
||||
Adapter = lokale Netzwerkkarte bzw. lokale IP. Leer bedeutet automatische Auswahl durch das Betriebssystem.
|
||||
<div class="info-card">
|
||||
Adapter = lokale Netzwerkkarte bzw. lokale IP. Leer bedeutet automatische Auswahl durch das Betriebssystem.
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-panel active" data-settings-panel="midi">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>MIDI</h2>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>MIDI Input</span>
|
||||
<select id="midiInputSelect"></select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>MIDI Output</span>
|
||||
<select id="midiOutputSelect"></select>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>MIDI</h2>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>MIDI Input</span>
|
||||
<select id="midiInputSelect"></select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>MIDI Output</span>
|
||||
<select id="midiOutputSelect"></select>
|
||||
</label>
|
||||
</section>
|
||||
<div class="settings-tab-panel" data-settings-panel="audio">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Audio</h2>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>Audio Output</span>
|
||||
<select id="audioOutputSelect"></select>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Audio</h2>
|
||||
</div>
|
||||
<div class="settings-tab-panel" data-settings-panel="artnet">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Art-Net</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Art-Net aktiv</span>
|
||||
<input id="artnetEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="artnetAdapterSelect"></select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Art-Net Ziel</span>
|
||||
<input id="artnetHostInput" type="text" placeholder="255.255.255.255">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Audio Output</span>
|
||||
<select id="audioOutputSelect"></select>
|
||||
<span>Port</span>
|
||||
<input id="artnetPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</section>
|
||||
<label class="field">
|
||||
<span>Universe</span>
|
||||
<input id="artnetUniverseInput" type="number" min="0" max="32767">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Timecode Port</span>
|
||||
<input id="artnetTimecodePortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
<label class="field checkbox">
|
||||
<span>Broadcast</span>
|
||||
<input id="artnetBroadcastInput" type="checkbox">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Art-Net</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Art-Net aktiv</span>
|
||||
<input id="artnetEnabledInput" type="checkbox">
|
||||
<div class="settings-tab-panel" data-settings-panel="sacn">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>sACN</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>sACN aktiv</span>
|
||||
<input id="sacnEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="sacnAdapterSelect"></select>
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>sACN Host</span>
|
||||
<input id="sacnHostInput" type="text" placeholder="239.255.x.x oder Node-IP">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="artnetAdapterSelect"></select>
|
||||
<span>sACN Port</span>
|
||||
<input id="sacnPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Art-Net Ziel</span>
|
||||
<input id="artnetHostInput" type="text" placeholder="255.255.255.255">
|
||||
<span>sACN Source</span>
|
||||
<input id="sacnSourceNameInput" type="text" placeholder="Time4Midi">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Port</span>
|
||||
<input id="artnetPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Universe</span>
|
||||
<input id="artnetUniverseInput" type="number" min="0" max="32767">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Timecode Port</span>
|
||||
<input id="artnetTimecodePortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
<label class="field checkbox">
|
||||
<span>Broadcast</span>
|
||||
<input id="artnetBroadcastInput" type="checkbox">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
<label class="field checkbox">
|
||||
<span>Multicast</span>
|
||||
<input id="sacnMulticastInput" type="checkbox">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>sACN</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>sACN aktiv</span>
|
||||
<input id="sacnEnabledInput" type="checkbox">
|
||||
<div class="settings-tab-panel" data-settings-panel="osc">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>OSC</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>OSC aktiv</span>
|
||||
<input id="oscEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="oscAdapterSelect"></select>
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>OSC Host</span>
|
||||
<input id="oscHostInput" type="text" placeholder="127.0.0.1">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="sacnAdapterSelect"></select>
|
||||
<span>OSC Port</span>
|
||||
<input id="oscPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>sACN Host</span>
|
||||
<input id="sacnHostInput" type="text" placeholder="239.255.x.x oder Node-IP">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>sACN Port</span>
|
||||
<input id="sacnPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>sACN Source</span>
|
||||
<input id="sacnSourceNameInput" type="text" placeholder="Time4Midi">
|
||||
</label>
|
||||
<label class="field checkbox">
|
||||
<span>Multicast</span>
|
||||
<input id="sacnMulticastInput" type="checkbox">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>OSC</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>OSC aktiv</span>
|
||||
<input id="oscEnabledInput" type="checkbox">
|
||||
<div class="settings-tab-panel" data-settings-panel="mqtt">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>MQTT</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>MQTT aktiv</span>
|
||||
<input id="mqttEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="mqttAdapterSelect"></select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>MQTT Broker</span>
|
||||
<input id="mqttBrokerInput" type="text" placeholder="mqtt://127.0.0.1:1883">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>MQTT User</span>
|
||||
<input id="mqttUsernameInput" type="text">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="oscAdapterSelect"></select>
|
||||
<span>MQTT Password</span>
|
||||
<input id="mqttPasswordInput" type="password">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>OSC Host</span>
|
||||
<input id="oscHostInput" type="text" placeholder="127.0.0.1">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>OSC Port</span>
|
||||
<input id="oscPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>MQTT Client ID</span>
|
||||
<input id="mqttClientIdInput" type="text" placeholder="time4midi-desktop">
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>MQTT</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>MQTT aktiv</span>
|
||||
<input id="mqttEnabledInput" type="checkbox">
|
||||
<div class="settings-tab-panel" data-settings-panel="serial">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Serial</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Serial aktiv</span>
|
||||
<input id="serialEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Serial Port</span>
|
||||
<input id="serialPathInput" type="text" placeholder="COM3 oder /dev/ttyUSB0">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="mqttAdapterSelect"></select>
|
||||
<span>Baud</span>
|
||||
<input id="serialBaudInput" type="number" min="300" max="921600">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>MQTT Broker</span>
|
||||
<input id="mqttBrokerInput" type="text" placeholder="mqtt://127.0.0.1:1883">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>MQTT User</span>
|
||||
<input id="mqttUsernameInput" type="text">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>MQTT Password</span>
|
||||
<input id="mqttPasswordInput" type="password">
|
||||
</label>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>MQTT Client ID</span>
|
||||
<input id="mqttClientIdInput" type="text" placeholder="time4midi-desktop">
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Serial</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Serial aktiv</span>
|
||||
<input id="serialEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Serial Port</span>
|
||||
<input id="serialPathInput" type="text" placeholder="COM3 oder /dev/ttyUSB0">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Baud</span>
|
||||
<input id="serialBaudInput" type="number" min="300" max="921600">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Parity</span>
|
||||
<select id="serialParitySelect">
|
||||
<option value="none">None</option>
|
||||
<option value="even">Even</option>
|
||||
<option value="odd">Odd</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Mode</span>
|
||||
<select id="serialModeSelect">
|
||||
<option value="rs232">RS232</option>
|
||||
<option value="rs485">RS485</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Modbus TCP</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Modbus TCP aktiv</span>
|
||||
<input id="modbusTcpEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="modbusTcpAdapterSelect"></select>
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Modbus TCP Host</span>
|
||||
<input id="modbusTcpHostInput" type="text" placeholder="127.0.0.1">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Port</span>
|
||||
<input id="modbusTcpPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Modbus RTU</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Modbus RTU aktiv</span>
|
||||
<input id="modbusRtuEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Modbus RTU Port</span>
|
||||
<input id="modbusRtuPathInput" type="text" placeholder="COM4 oder /dev/ttyUSB1">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Baud</span>
|
||||
<input id="modbusRtuBaudInput" type="number" min="300" max="921600">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Parity</span>
|
||||
<select id="modbusRtuParitySelect">
|
||||
<select id="serialParitySelect">
|
||||
<option value="none">None</option>
|
||||
<option value="even">Even</option>
|
||||
<option value="odd">Odd</option>
|
||||
</select>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>KNX</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>KNX aktiv</span>
|
||||
<input id="knxEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="knxAdapterSelect"></select>
|
||||
<span>Mode</span>
|
||||
<select id="serialModeSelect">
|
||||
<option value="rs232">RS232</option>
|
||||
<option value="rs485">RS485</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>KNX IP</span>
|
||||
<input id="knxIpInput" type="text" placeholder="224.0.23.12 oder Gateway-IP">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Port</span>
|
||||
<input id="knxPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Phys Addr</span>
|
||||
<input id="knxPhysAddrInput" type="text" placeholder="15.15.15">
|
||||
</label>
|
||||
<label class="field checkbox-row">
|
||||
<span>Tunneling</span>
|
||||
<input id="knxTunnelingInput" type="checkbox">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-panel" data-settings-panel="timeline">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Timeline</h2>
|
||||
</div>
|
||||
<div id="timelineInspector"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-panel" data-settings-panel="selection">
|
||||
<div class="settings-tab-panel" data-settings-panel="modbusTcp">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Auswahl</h2>
|
||||
<h2>Modbus TCP</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Modbus TCP aktiv</span>
|
||||
<input id="modbusTcpEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="modbusTcpAdapterSelect"></select>
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Modbus TCP Host</span>
|
||||
<input id="modbusTcpHostInput" type="text" placeholder="127.0.0.1">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Port</span>
|
||||
<input id="modbusTcpPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-panel" data-settings-panel="modbusRtu">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>Modbus RTU</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>Modbus RTU aktiv</span>
|
||||
<input id="modbusRtuEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Modbus RTU Port</span>
|
||||
<input id="modbusRtuPathInput" type="text" placeholder="COM4 oder /dev/ttyUSB1">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Baud</span>
|
||||
<input id="modbusRtuBaudInput" type="number" min="300" max="921600">
|
||||
</label>
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>Parity</span>
|
||||
<select id="modbusRtuParitySelect">
|
||||
<option value="none">None</option>
|
||||
<option value="even">Even</option>
|
||||
<option value="odd">Odd</option>
|
||||
</select>
|
||||
</label>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="settings-tab-panel" data-settings-panel="knx">
|
||||
<section class="settings-section">
|
||||
<div class="panel-header">
|
||||
<h2>KNX</h2>
|
||||
</div>
|
||||
<label class="field checkbox-row">
|
||||
<span>KNX aktiv</span>
|
||||
<input id="knxEnabledInput" type="checkbox">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Netzwerkadapter</span>
|
||||
<select id="knxAdapterSelect"></select>
|
||||
</label>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>KNX IP</span>
|
||||
<input id="knxIpInput" type="text" placeholder="224.0.23.12 oder Gateway-IP">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Port</span>
|
||||
<input id="knxPortInput" type="number" min="1" max="65535">
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-grid">
|
||||
<label class="field">
|
||||
<span>Phys Addr</span>
|
||||
<input id="knxPhysAddrInput" type="text" placeholder="15.15.15">
|
||||
</label>
|
||||
<label class="field checkbox-row">
|
||||
<span>Tunneling</span>
|
||||
<input id="knxTunnelingInput" type="checkbox">
|
||||
</label>
|
||||
</div>
|
||||
<div id="selectionInspector"></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+82
-3
@@ -76,14 +76,15 @@ select {
|
||||
|
||||
.app-shell {
|
||||
display: grid;
|
||||
grid-template-columns: 286px minmax(0, 1fr);
|
||||
grid-template-columns: 286px minmax(0, 1fr) 308px;
|
||||
gap: 12px;
|
||||
min-height: 100vh;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.sidebar,
|
||||
.workspace {
|
||||
.workspace,
|
||||
.inspector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
@@ -239,6 +240,14 @@ p {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.truncate-text {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.timeline-list,
|
||||
.track-list,
|
||||
#timelineInspector,
|
||||
@@ -337,6 +346,7 @@ p {
|
||||
|
||||
.track-card-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.track-card-actions {
|
||||
@@ -379,6 +389,14 @@ p {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.inspector {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.inspector-panel {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.transport-bar {
|
||||
padding: 12px 14px;
|
||||
}
|
||||
@@ -404,6 +422,17 @@ p {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.editor-title-block {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#timelineTitle {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.canvas-frame {
|
||||
margin-top: 10px;
|
||||
flex: 1;
|
||||
@@ -476,8 +505,12 @@ p {
|
||||
}
|
||||
|
||||
.settings-tab {
|
||||
min-width: 92px;
|
||||
min-width: 0;
|
||||
max-width: 96px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.settings-tab.active {
|
||||
@@ -489,6 +522,8 @@ p {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding-right: 4px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.settings-tab-panel {
|
||||
@@ -538,9 +573,49 @@ p {
|
||||
}
|
||||
|
||||
@media (max-width: 1320px) {
|
||||
.app-shell {
|
||||
grid-template-columns: 262px minmax(0, 1fr) 280px;
|
||||
}
|
||||
|
||||
.settings-dialog {
|
||||
width: min(1040px, calc(100vw - 32px));
|
||||
}
|
||||
}
|
||||
|
||||
.track-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.track-card-header strong {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.meta-row.compact {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.meta-row.compact span:first-child {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.timeline-card strong {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 1180px) {
|
||||
.app-shell {
|
||||
grid-template-columns: 270px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.inspector {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
@@ -555,6 +630,10 @@ p {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.inspector {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.field-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user