nodeskini 1.0.6 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,11 @@
53
53
  'height=400, width=600, top=200, left=300, toolbar=no, menubar=no, directories=no, location=no, resizable=yes, scrollbars=yes, status=no');
54
54
  return false;">Parameters</button>
55
55
 
56
- <button class="small button" id="launchSimulator"
56
+ <button type="button" class="small button" onclick="window.open('http://' + window.location.hostname + ':' + window.location.port + '/queueViewer','Queue Viewer',
57
+ 'height=600, width=1200, top=200, left=300, toolbar=no, menubar=no, directories=no, location=no, resizable=yes, scrollbars=yes, status=no');
58
+ return false;">Queue Viewer</button>
59
+
60
+ <button class="small button" id="launchSimulator"
57
61
  onclick="launchSimulator()">Start Simulator</button>
58
62
  <button id="stopSimulator" class="small button red" style="display:none"
59
63
  onclick="stopSimulator();">Stop Simulator</button>
@@ -2797,12 +2797,11 @@ export function setServ(ser, daw, groupeCS, oscMidi, mix){
2797
2797
 
2798
2798
  function setTempo(value, param){
2799
2799
  tempoGlobal = value;
2800
-
2801
- // if(midimix.getAbletonLinkStatus()) {
2802
- // if(debug) console.log("ORCHESTRATION: set tempo Link:", value);
2803
- // midimix.setTempoLink(value);
2804
- // return;
2805
- // }
2800
+ if(midimix.getAbletonLinkStatus()) {
2801
+ if(debug) console.log("ORCHESTRATION: set tempo Link:", value);
2802
+ midimix.setTempoLink(value);
2803
+ return;
2804
+ }
2806
2805
 
2807
2806
  if ( value > tempoMax || value < tempoMin) {
2808
2807
  console.log("ERR: Tempo set out of range:", value, "Should be between:", tempoMin, "and", tempoMax);
@@ -53,6 +53,10 @@
53
53
  'height=400, width=600, top=200, left=300, toolbar=no, menubar=no, directories=no, location=no, resizable=yes, scrollbars=yes, status=no');
54
54
  return false;">Parameters</button>
55
55
 
56
+ <button type="button" class="button" onclick="window.open('http://' + window.location.hostname + ':' + window.location.port + '/queueViewer','Queue Viewer',
57
+ 'height=400, width=1200, top=200, left=300, toolbar=no, menubar=no, directories=no, location=no, resizable=yes, scrollbars=yes, status=no');
58
+ return false;">Queue Viewer</button>
59
+
56
60
  <button class="button" id="launchSimulator" onclick="launchSimulator()">Start Simulator</button>
57
61
  <button id="stopSimulator" class="small button red" style="display:none" onclick="stopSimulator();">Stop
58
62
  Simulator</button>
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>Files d'attente - Queue Viewer</title>
7
+ <link rel="stylesheet" href="/client/queueViewer/style.css" />
8
+ <meta name="robots" content="noindex" />
9
+ </head>
10
+ <body>
11
+ <header>
12
+ <h1>Files d'attente (dynamique)</h1>
13
+ <p id="status">Connexion : en attente...</p>
14
+ <div class="controls">
15
+ <button id="pauseButton" type="button">Arrêt sur image</button>
16
+ <span id="pauseState">En direct</span>
17
+ </div>
18
+ <p id="tickInfo">Tick : 0</p>
19
+ </header>
20
+
21
+ <main>
22
+ <div id="queuesContainer">
23
+ <table id="queuesTable">
24
+ <thead>
25
+ <tr><th>File</th><th>Longueur</th><th>Contenu</th></tr>
26
+ </thead>
27
+ <tbody></tbody>
28
+ </table>
29
+ </div>
30
+ </main>
31
+
32
+ <script src="/client/queueViewer/queue.js"></script>
33
+ </body>
34
+ </html>
@@ -0,0 +1,187 @@
1
+ // Client minimal pour afficher dynamiquement les files d'attente
2
+ (async function(){
3
+ const statusEl = () => document.getElementById('status');
4
+ const tickInfoEl = () => document.getElementById('tickInfo');
5
+ const pauseStateEl = () => document.getElementById('pauseState');
6
+ const pauseButtonEl = () => document.getElementById('pauseButton');
7
+ let paused = false;
8
+ let queuedQueues = null;
9
+
10
+ // Récupère la config du serveur pour le port websocket
11
+ let ipConfig = { websocketServeurPort: 8383 };
12
+ try {
13
+ const r = await fetch('/serveur/ipConfig.json');
14
+ if (r.ok) ipConfig = await r.json();
15
+ } catch(e) {
16
+ console.warn('Impossible de charger /serveur/ipConfig.json, port par défaut utilisé');
17
+ }
18
+
19
+ const host = window.location.hostname || 'localhost';
20
+ const port = ipConfig.websocketServeurPort || 8383;
21
+ let ws;
22
+
23
+ // Ce visionneur est proche du controleur fonctionnellement
24
+ function connect(){
25
+ statusEl().textContent = `Connexion : ws://${host}:${port}`;
26
+ ws = new WebSocket(`ws://${host}:${port}`);
27
+
28
+ ws.onopen = function(){
29
+ statusEl().textContent = `Connecté à ${host}:${port}`;
30
+ const msg = { type: 'startSpectateur', text: 'queueViewer', id: Date.now() };
31
+ ws.send(JSON.stringify(msg));
32
+ };
33
+
34
+ ws.onclose = function(){
35
+ statusEl().textContent = 'Déconnecté, reconnexion dans 2s...';
36
+ setTimeout(connect, 2000);
37
+ };
38
+
39
+ ws.onerror = function(e){
40
+ console.error('WebSocket error', e);
41
+ };
42
+
43
+ ws.onmessage = function(event){
44
+ try {
45
+ const msg = JSON.parse(event.data);
46
+ if (msg.type === 'etatDeLaFileAttente') {
47
+ if (paused) {
48
+ queuedQueues = msg.value || [];
49
+ statusEl().textContent = `Pause activée - frame figée`;
50
+ } else {
51
+ renderQueues(msg.value || []);
52
+ }
53
+ }
54
+ else if (msg.type === 'setTickAutomate') {
55
+ tickInfoEl().textContent = `Tick : ${msg.tick}`;
56
+ }
57
+ } catch(e){
58
+ console.warn('Erreur parsing message', e);
59
+ }
60
+ };
61
+ }
62
+
63
+ function renderQueues(queues){
64
+ const tbody = document.querySelector('#queuesTable tbody');
65
+ tbody.innerHTML = '';
66
+
67
+ // First pass: compute durations for all clips and per-queue totals
68
+ const queuesInfo = [];
69
+ for (let i = 0; i < queues.length; i++){
70
+ const q = queues[i];
71
+ const index = q && q[0] !== undefined ? q[0] : i;
72
+ const content = (q && q[2]) ? q[2] : [];
73
+ const clips = content.map(it => {
74
+ if (it && typeof it === 'object') return it;
75
+ return { name: String(it) };
76
+ });
77
+ const durations = clips.map(c => {
78
+ if (Array.isArray(c) && c[2] && typeof c[2] === 'number') return c[2];
79
+ if (c.duration && typeof c.duration === 'number') return c.duration;
80
+ return 1;
81
+ });
82
+ const total = durations.reduce((s, v) => s + v, 0) || clips.length;
83
+ queuesInfo.push({ index, clips, durations, total });
84
+ }
85
+
86
+ // Global timeline length = max of queue totals (so longest queue spans full timeline)
87
+ const globalMaxTotal = queuesInfo.reduce((m, q) => Math.max(m, q.total || 0), 0);
88
+ const displayTicks = globalMaxTotal || 0;
89
+ const effectiveMaxTotal = globalMaxTotal || 1;
90
+ //tickInfoEl().textContent = `Tick : ${displayTicks}`;
91
+
92
+ // Fixed width for all timelines (same scale across all files)
93
+ const FIXED_TIMELINE_WIDTH = 920;
94
+
95
+ // Build map of queues by index so missing indices can be rendered empty
96
+ const queueMap = new Map();
97
+ let maxIndex = -1;
98
+ queuesInfo.forEach(info => {
99
+ queueMap.set(info.index, info);
100
+ if (info.index > maxIndex) maxIndex = info.index;
101
+ });
102
+
103
+ // Second pass: render rows using a fixed timeline width so proportions match across rows
104
+ for (let idx = 0; idx <= maxIndex; idx++){
105
+ const info = queueMap.get(idx) || { index: idx, clips: [], durations: [], total: 0 };
106
+ const tr = document.createElement('tr');
107
+ const tdIndex = document.createElement('td'); tdIndex.textContent = info.index;
108
+ const tdLength = document.createElement('td'); tdLength.textContent = info.clips.length;
109
+ const tdContent = document.createElement('td');
110
+
111
+ const timeline = document.createElement('div');
112
+ timeline.className = 'timeline';
113
+ timeline.style.width = FIXED_TIMELINE_WIDTH + 'px';
114
+
115
+ if (info.clips.length === 0) {
116
+ timeline.classList.add('timelineEmpty');
117
+ const emptyEl = document.createElement('div');
118
+ emptyEl.className = 'emptySlot';
119
+ emptyEl.textContent = 'vide';
120
+ timeline.appendChild(emptyEl);
121
+ } else {
122
+ for (let j = 0; j < info.clips.length; j++){
123
+ const c = info.clips[j];
124
+ const dur = info.durations[j] || 1;
125
+ const widthPx = Math.max(24, Math.round((dur / effectiveMaxTotal) * FIXED_TIMELINE_WIDTH));
126
+ const label = (Array.isArray(c) && c[1]) ? c[1] : (c.name || c.label || 'clip');
127
+ const clipEl = document.createElement('div');
128
+ clipEl.className = label === 'clip' ? 'clip clipPlaceholder' : 'clip';
129
+ clipEl.style.flex = 'none';
130
+ clipEl.style.width = widthPx + 'px';
131
+ clipEl.title = label;
132
+ if (label !== 'clip') {
133
+ // Use queue index (vertical type) for color instead of clip name
134
+ clipEl.style.backgroundColor = colorFromQueueIndex(info.index);
135
+ }
136
+ const span = document.createElement('span'); span.textContent = label;
137
+ clipEl.appendChild(span);
138
+ timeline.appendChild(clipEl);
139
+ }
140
+ }
141
+
142
+ tdContent.appendChild(timeline);
143
+ tr.appendChild(tdIndex);
144
+ tr.appendChild(tdLength);
145
+ tr.appendChild(tdContent);
146
+ tbody.appendChild(tr);
147
+ }
148
+ }
149
+
150
+ // Simple color hash from string
151
+ function colorFromString(s){
152
+ let h = 0;
153
+ for (let i = 0; i < s.length; i++) h = (h<<5) - h + s.charCodeAt(i);
154
+ h = Math.abs(h) % 360;
155
+ return `hsl(${h}, 65%, 45%)`;
156
+ }
157
+
158
+ // Color from queue index (vertical type)
159
+ function colorFromQueueIndex(idx){
160
+ const hue = (idx * 45) % 360;
161
+ return `hsl(${hue}, 70%, 50%)`;
162
+ }
163
+
164
+ function initControls(){
165
+ const button = pauseButtonEl();
166
+ button.addEventListener('click', () => {
167
+ paused = !paused;
168
+ if (paused) {
169
+ pauseStateEl().textContent = 'Figé';
170
+ button.textContent = 'Reprendre';
171
+ statusEl().textContent = 'Arrêt sur image activé';
172
+ } else {
173
+ pauseStateEl().textContent = 'En direct';
174
+ button.textContent = 'Arrêt sur image';
175
+ statusEl().textContent = `Connecté à ${host}:${port}`;
176
+ if (queuedQueues !== null) {
177
+ renderQueues(queuedQueues);
178
+ queuedQueues = null;
179
+ }
180
+ }
181
+ });
182
+ }
183
+
184
+ connect();
185
+ initControls();
186
+
187
+ })();
@@ -0,0 +1,26 @@
1
+ body{font-family: Arial, Helvetica, sans-serif; margin:16px; color:#222}
2
+ header h1{margin:0 0 6px 0}
3
+ #status{font-size:0.9rem; color:#666}
4
+ .controls{margin:8px 0; display:flex; gap:10px; align-items:center;}
5
+ #pauseButton{padding:6px 12px; border:1px solid #888; border-radius:4px; background:#f8f8f8; cursor:pointer;}
6
+ #pauseButton:hover{background:#eee}
7
+ #pauseState{font-size:0.9rem; color:#444}
8
+ #tickInfo{font-size:0.9rem; color:#444; margin:4px 0 0 0}
9
+ #queuesContainer{margin-top:12px; overflow-x:auto}
10
+ table{border-collapse:collapse; width:100%; max-width:100%}
11
+ th,td{border:1px solid #ddd; padding:8px; text-align:left}
12
+ th{background:#f3f3f3}
13
+ th:nth-child(2), td:nth-child(2){width:90px; white-space:nowrap}
14
+ body{background:#fff}
15
+ tbody tr:nth-child(odd){background:#fafafa}
16
+ tbody tr:nth-child(odd) td:last-child,
17
+ tbody tr:nth-child(even) td:last-child{background:#fff}
18
+
19
+ .timeline{display:flex; gap:6px; align-items:center; height:28px; background:transparent; border-radius:4px;}
20
+ .timeline.timelineEmpty{background:rgba(0,0,0,0.06); border:1px dashed rgba(0,0,0,0.15);}
21
+ .emptySlot{flex:1; min-height:24px; display:flex; align-items:center; justify-content:center; color:#666; font-size:12px; font-style:italic;}
22
+ .clip{height:24px; border-radius:3px; box-shadow:0 1px 0 rgba(0,0,0,0.15) inset; overflow:hidden; display:inline-flex; align-items:center; justify-content:center; color:rgba(255,255,255,0.95); font-size:12px; padding:0 8px; white-space:nowrap; text-overflow:ellipsis}
23
+ .clipPlaceholder{background:rgba(255,255,255,0.15); color:rgba(0,0,0,0.6); border:1px dashed rgba(0,0,0,0.12);}
24
+ .clip span{display:inline-block; overflow:hidden; text-overflow:ellipsis}
25
+ .clip:hover{transform:translateY(-2px); box-shadow:0 4px 8px rgba(0,0,0,0.15)}
26
+
@@ -77,7 +77,7 @@ var textSize = 20 * screenY / Ybase;
77
77
 
78
78
  // La version processing.min.js ne sais pas gérer les couleur en hexa.
79
79
  function hex_to_RGB(hex) {
80
- console.log("hex_to_RGB:", typeof hex, hex);
80
+ if (debug) console.log("hex_to_RGB:", typeof hex, hex);
81
81
  if(hex == '') return [0,0,0];
82
82
 
83
83
  let m = hex.match(/^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);
@@ -608,6 +608,8 @@ function isInOngoingGraphicScene(scene){
608
608
  }
609
609
 
610
610
  function addSceneScore(scene){
611
+ if(debug1) console.log("addSceneScore:", scene, ongoingGraphicScene);
612
+
611
613
  // Déjà là
612
614
  for (var i=0; i < ongoingGraphicScene.length; i++){
613
615
  if ( ongoingGraphicScene[i] === scene){
@@ -626,17 +628,15 @@ function addSceneScore(scene){
626
628
  }
627
629
 
628
630
  function removeSceneScore(scene){
629
- if( ongoingGraphicScene[ongoingGraphicScene.length - 1] === scene){
630
- ongoingGraphicScene.pop();
631
- return true;
632
- }else{
633
- for (var i=0; i < ongoingGraphicScene.length; i++){
634
- if ( ongoingGraphicScene[i] === scene){
635
- ongoingGraphicScene[i] = -1;
636
- return true;
637
- }
631
+ if(debug1) console.log("removeSceneScore:", scene, ongoingGraphicScene);
632
+
633
+ for (var i = ongoingGraphicScene.length - 1; i >= 0; i--){
634
+ if ( ongoingGraphicScene[i] === scene){
635
+ ongoingGraphicScene.splice(i, 1);
636
+ return true;
638
637
  }
639
638
  }
639
+ return false;
640
640
  }
641
641
 
642
642
  /**************************
@@ -716,7 +716,7 @@ function sketchProc(processing) {
716
716
  groupsCounter++;
717
717
  }else if(patternGroups[i][2] == "tank"){
718
718
  if (debug) console.log("This one is the tank:", patternGroups[i][0]);
719
- if ( i > 0){
719
+ if (groupsCounter > 0){
720
720
  // C'est ici que l'on gére les tanks de façon séquentielle
721
721
  // On regarde si le prédécesseur est dans le même tank.
722
722
  // Cela pose une contrainte sur le fichier de configuration graphique.
@@ -78,7 +78,7 @@ var textSize = 20 * screenY / Ybase;
78
78
 
79
79
  // La version processing.min.js ne sais pas gérer les couleur en hexa.
80
80
  function hex_to_RGB(hex) {
81
- console.log("hex_to_RGB:", typeof hex, hex);
81
+ if (debug) console.log("hex_to_RGB:", typeof hex, hex);
82
82
  if(hex == '') return [0,0,0];
83
83
 
84
84
  let m = hex.match(/^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i);
@@ -609,6 +609,8 @@ function isInOngoingGraphicScene(scene){
609
609
  }
610
610
 
611
611
  function addSceneScore(scene){
612
+ if(debug1) console.log("addSceneScore:", scene, ongoingGraphicScene);
613
+
612
614
  // Déjà là
613
615
  for (var i=0; i < ongoingGraphicScene.length; i++){
614
616
  if ( ongoingGraphicScene[i] === scene){
@@ -627,17 +629,15 @@ function addSceneScore(scene){
627
629
  }
628
630
 
629
631
  function removeSceneScore(scene){
630
- if( ongoingGraphicScene[ongoingGraphicScene.length - 1] === scene){
631
- ongoingGraphicScene.pop();
632
- return true;
633
- }else{
634
- for (var i=0; i < ongoingGraphicScene.length; i++){
635
- if ( ongoingGraphicScene[i] === scene){
636
- ongoingGraphicScene[i] = -1;
637
- return true;
638
- }
632
+ if(debug1) console.log("removeSceneScore:", scene, ongoingGraphicScene);
633
+
634
+ for (var i = ongoingGraphicScene.length - 1; i >= 0; i--){
635
+ if ( ongoingGraphicScene[i] === scene){
636
+ ongoingGraphicScene.splice(i, 1);
637
+ return true;
639
638
  }
640
639
  }
640
+ return false;
641
641
  }
642
642
 
643
643
  /**************************
@@ -717,7 +717,7 @@ function sketchProc(processing) {
717
717
  groupsCounter++;
718
718
  }else if(patternGroups[i][2] == "tank"){
719
719
  if (debug) console.log("This one is the tank:", patternGroups[i][0]);
720
- if ( i > 0){
720
+ if (groupsCounter > 0){
721
721
  // C'est ici que l'on gére les tanks de façon séquentielle
722
722
  // On regarde si le prédécesseur est dans le même tank.
723
723
  // Cela pose une contrainte sur le fichier de configuration graphique.
@@ -1,15 +1,16 @@
1
+ <!doctype html>
1
2
  <html>
2
3
  <head>
3
- <script src="./client/score/processing.min.js"></script>
4
- <script src="./client/score/parto1bundle.js"></script>
5
- <meta charset="UTF-8">
6
- </head>
7
- <body onload="initWSSocket(window.location.hostname);">
4
+ <meta charset="UTF-8">
5
+ <title>Score</title>
6
+ <script src="/client/score/processing.min.js"></script>
7
+ <script src="/client/score/parto1bundle.js"></script>
8
+ </head>
9
+ <body onload="initWSSocket(window.location.hostname);">
8
10
  <h1>Score</h1>
9
11
  <input id="buttonUpdate" class="button" type="button" value="Update" onclick="getPatternGroups();">
10
-
11
12
  <p><canvas id="canvas1" width="200" height="200"></canvas></p>
12
- </body>
13
+ </body>
13
14
  </html>
14
15
 
15
16
 
Binary file