|
26 | 26 |
|
27 | 27 | // Code preview state (declared early — referenced by subscription below) |
28 | 28 | let showCode = $state(false); |
| 29 | + let showPortLabels = $state(false); |
29 | 30 | let previewCode = $state(''); |
30 | 31 | let editorContainer = $state<HTMLDivElement | undefined>(undefined); |
31 | 32 | let editorView: import('@codemirror/view').EditorView | null = null; |
|
51 | 52 | node = graphStore.getNode(id) || null; |
52 | 53 | // Reset to properties view when opening a new node |
53 | 54 | showCode = false; |
| 55 | + showPortLabels = false; |
54 | 56 | destroyEditor(); |
55 | 57 | } else { |
56 | 58 | node = null; |
57 | 59 | showCode = false; |
| 60 | + showPortLabels = false; |
58 | 61 | destroyEditor(); |
59 | 62 | } |
60 | 63 | }); |
|
146 | 149 | const blockCode = generateBlockCode(node, allNodes, allConnections); |
147 | 150 | previewCode = header + blockCode; |
148 | 151 | copied = false; |
| 152 | + showPortLabels = false; |
149 | 153 | showCode = true; |
150 | 154 | setTimeout(() => initEditor(), 0); |
151 | 155 | } |
152 | 156 | } |
153 | 157 |
|
| 158 | + // Toggle port-labels view (same mutual-exclusion pattern as code view) |
| 159 | + function togglePortLabelsView() { |
| 160 | + if (showPortLabels) { |
| 161 | + showPortLabels = false; |
| 162 | + } else { |
| 163 | + showCode = false; |
| 164 | + destroyEditor(); |
| 165 | + showPortLabels = true; |
| 166 | + } |
| 167 | + } |
| 168 | +
|
154 | 169 | function copyToClipboard() { |
155 | 170 | navigator.clipboard.writeText(previewCode); |
156 | 171 | copied = true; |
|
184 | 199 | // itself is the source of truth and editing port names directly would be |
185 | 200 | // overwritten on the next param change. |
186 | 201 | const hasParamDrivenPortLabels = $derived(node ? getPortLabelConfigs(node.type).length > 0 : false); |
187 | | - const showPortLabels = $derived( |
| 202 | + const hasEditablePortLabels = $derived( |
188 | 203 | !!node && !hasParamDrivenPortLabels && (node.inputs.length > 0 || node.outputs.length > 0) |
189 | 204 | ); |
190 | 205 |
|
|
276 | 291 | <div class="dialog-header"> |
277 | 292 | {#if showCode} |
278 | 293 | <span id="dialog-title">Python Code</span> |
| 294 | + {:else if showPortLabels} |
| 295 | + <span id="dialog-title">Port Labels</span> |
279 | 296 | {:else} |
280 | 297 | <div class="node-info"> |
281 | 298 | <input |
|
306 | 323 | <Icon name="copy" size={16} /> |
307 | 324 | {/if} |
308 | 325 | </button> |
309 | | - {:else} |
| 326 | + {:else if !showPortLabels} |
310 | 327 | <!-- Color picker --> |
311 | 328 | <ColorPicker |
312 | 329 | currentColor={currentColor} |
|
348 | 365 | </button> |
349 | 366 | {/if} |
350 | 367 | {/if} |
351 | | - <!-- Toggle code view button --> |
352 | | - <button |
353 | | - class="icon-btn" |
354 | | - onclick={toggleCodeView} |
355 | | - use:tooltip={showCode ? "View Properties" : "View Python Code"} |
356 | | - aria-label={showCode ? "View Properties" : "View Python Code"} |
357 | | - > |
358 | | - <Icon name={showCode ? "settings" : "braces"} size={16} /> |
359 | | - </button> |
| 368 | + <!-- Toggle port labels view button (hidden in code view) --> |
| 369 | + {#if showPortLabels || (!showCode && hasEditablePortLabels)} |
| 370 | + <button |
| 371 | + class="icon-btn" |
| 372 | + onclick={togglePortLabelsView} |
| 373 | + use:tooltip={showPortLabels ? "View Properties" : "Edit Port Labels"} |
| 374 | + aria-label={showPortLabels ? "View Properties" : "Edit Port Labels"} |
| 375 | + > |
| 376 | + <Icon name={showPortLabels ? "settings" : "tag"} size={16} /> |
| 377 | + </button> |
| 378 | + {/if} |
| 379 | + <!-- Toggle code view button (hidden in port-labels view) --> |
| 380 | + {#if !showPortLabels} |
| 381 | + <button |
| 382 | + class="icon-btn" |
| 383 | + onclick={toggleCodeView} |
| 384 | + use:tooltip={showCode ? "View Properties" : "View Python Code"} |
| 385 | + aria-label={showCode ? "View Properties" : "View Python Code"} |
| 386 | + > |
| 387 | + <Icon name={showCode ? "settings" : "braces"} size={16} /> |
| 388 | + </button> |
| 389 | + {/if} |
360 | 390 | <button class="icon-btn" onclick={closeNodeDialog} aria-label="Close"> |
361 | 391 | <Icon name="x" size={16} /> |
362 | 392 | </button> |
|
371 | 401 | <div class="loading">Loading...</div> |
372 | 402 | {/if} |
373 | 403 | </div> |
| 404 | + {:else if showPortLabels} |
| 405 | + <!-- Port labels view --> |
| 406 | + {#if node.inputs.length === 0 && node.outputs.length === 0} |
| 407 | + <div class="no-params">No ports to label</div> |
| 408 | + {:else} |
| 409 | + <div class="section"> |
| 410 | + <div class="params-grid"> |
| 411 | + {#each node.inputs as port, i (port.id)} |
| 412 | + <div class="param-item"> |
| 413 | + <label for="port-in-{i}">in {i}</label> |
| 414 | + <input |
| 415 | + id="port-in-{i}" |
| 416 | + type="text" |
| 417 | + value={port.name} |
| 418 | + placeholder={PORT_NAME.input(i)} |
| 419 | + onchange={(e) => handlePortNameChange('input', i, e.currentTarget.value)} |
| 420 | + /> |
| 421 | + </div> |
| 422 | + {/each} |
| 423 | + {#each node.outputs as port, i (port.id)} |
| 424 | + <div class="param-item"> |
| 425 | + <label for="port-out-{i}">out {i}</label> |
| 426 | + <input |
| 427 | + id="port-out-{i}" |
| 428 | + type="text" |
| 429 | + value={port.name} |
| 430 | + placeholder={PORT_NAME.output(i)} |
| 431 | + onchange={(e) => handlePortNameChange('output', i, e.currentTarget.value)} |
| 432 | + /> |
| 433 | + </div> |
| 434 | + {/each} |
| 435 | + </div> |
| 436 | + </div> |
| 437 | + {/if} |
374 | 438 | {:else} |
375 | 439 | <!-- Parameters --> |
376 | 440 | {#if typeDef.params.length > 0} |
|
428 | 492 | <div class="no-params">No configurable parameters</div> |
429 | 493 | {/if} |
430 | 494 |
|
431 | | - <!-- Port labels — customise the names shown on each handle --> |
432 | | - {#if showPortLabels} |
433 | | - <div class="section"> |
434 | | - <div class="section-title">Port labels</div> |
435 | | - <div class="params-grid"> |
436 | | - {#each node.inputs as port, i (port.id)} |
437 | | - <div class="param-item"> |
438 | | - <label for="port-in-{i}">in {i}</label> |
439 | | - <input |
440 | | - id="port-in-{i}" |
441 | | - type="text" |
442 | | - value={port.name} |
443 | | - placeholder={PORT_NAME.input(i)} |
444 | | - onchange={(e) => handlePortNameChange('input', i, e.currentTarget.value)} |
445 | | - /> |
446 | | - </div> |
447 | | - {/each} |
448 | | - {#each node.outputs as port, i (port.id)} |
449 | | - <div class="param-item"> |
450 | | - <label for="port-out-{i}">out {i}</label> |
451 | | - <input |
452 | | - id="port-out-{i}" |
453 | | - type="text" |
454 | | - value={port.name} |
455 | | - placeholder={PORT_NAME.output(i)} |
456 | | - onchange={(e) => handlePortNameChange('output', i, e.currentTarget.value)} |
457 | | - /> |
458 | | - </div> |
459 | | - {/each} |
460 | | - </div> |
461 | | - </div> |
462 | | - {/if} |
463 | | - |
464 | 495 | <!-- Documentation section (lazy loaded) --> |
465 | 496 | <DocumentationSection docstringHtml={docstringHtml} /> |
466 | 497 | {/if} |
467 | 498 | </div> |
468 | 499 |
|
469 | | - {#if !showCode} |
| 500 | + {#if !showCode && !showPortLabels} |
470 | 501 | <div class="dialog-footer"> |
471 | 502 | <span class="hint">R rotate · X flip horizontal · Y flip vertical</span> |
472 | 503 | </div> |
|
0 commit comments