@hlang-tech/editor-ui 0.3.0 → 0.5.0

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.
@@ -0,0 +1,21 @@
1
+ /** LivePulse — one flash per run of a node, over its own rounded outline. */
2
+
3
+ .hl-live-pulse {
4
+ position: absolute;
5
+ inset: 0;
6
+ border-radius: inherit;
7
+ pointer-events: none;
8
+ box-shadow:
9
+ 0 0 0 2px var(--hl-spark),
10
+ 0 0 36px -4px var(--hl-spark);
11
+ opacity: 0;
12
+ animation: hl-live-pulse 0.6s var(--hl-ease);
13
+ }
14
+ @keyframes hl-live-pulse {
15
+ 0% {
16
+ opacity: 1;
17
+ }
18
+ 100% {
19
+ opacity: 0;
20
+ }
21
+ }
@@ -27,13 +27,6 @@
27
27
  box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
28
28
  0 0 0 1px color-mix(in oklab, var(--hl-spark) 70%, transparent),
29
29
  0 0 28px -6px var(--hl-spark);
30
- animation: hl-asset-pulse 0.7s var(--hl-ease);
31
- }
32
- @keyframes hl-asset-pulse {
33
- 0% {
34
- box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
35
- 0 0 0 2px var(--hl-spark), 0 0 36px -4px var(--hl-spark);
36
- }
37
30
  }
38
31
  .hl-asset.is-live-error {
39
32
  box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
@@ -144,3 +137,12 @@
144
137
  background: var(--hl-silver-hi);
145
138
  transform: scale(1.2);
146
139
  }
140
+
141
+ /* A node that has finished (RFC 0001): its inputs ended and its outputs
142
+ closed. It steps back, calmly, rather than going dark. */
143
+ .hl-asset {
144
+ transition: opacity 0.4s var(--hl-ease);
145
+ }
146
+ .hl-asset.is-finished {
147
+ opacity: 0.6;
148
+ }
@@ -50,13 +50,6 @@
50
50
  box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
51
51
  0 0 0 1px color-mix(in oklab, var(--hl-spark) 70%, transparent),
52
52
  0 0 28px -6px var(--hl-spark);
53
- animation: hl-node-pulse 0.7s var(--hl-ease);
54
- }
55
- @keyframes hl-node-pulse {
56
- 0% {
57
- box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
58
- 0 0 0 2px var(--hl-spark), 0 0 36px -4px var(--hl-spark);
59
- }
60
53
  }
61
54
  /* A node that threw during the run goes coral. */
62
55
  .hl-node.is-live-error {
@@ -190,3 +183,12 @@
190
183
  .hl-port.is-error .hl-handle.hl-handle:hover {
191
184
  background: var(--hl-error);
192
185
  }
186
+
187
+ /* A node that has finished (RFC 0001): its inputs ended and its outputs
188
+ closed. It steps back, calmly, rather than going dark. */
189
+ .hl-node {
190
+ transition: opacity 0.4s var(--hl-ease);
191
+ }
192
+ .hl-node.is-finished {
193
+ opacity: 0.6;
194
+ }
@@ -40,13 +40,6 @@
40
40
  box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
41
41
  0 0 0 1px color-mix(in oklab, var(--hl-spark) 70%, transparent),
42
42
  0 0 26px -6px var(--hl-spark);
43
- animation: hl-pill-pulse 0.7s var(--hl-ease);
44
- }
45
- @keyframes hl-pill-pulse {
46
- 0% {
47
- box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
48
- 0 0 0 2px var(--hl-spark), 0 0 34px -4px var(--hl-spark);
49
- }
50
43
  }
51
44
  .hl-pill.is-live-error {
52
45
  box-shadow: var(--hl-glass-edge-hi), var(--hl-shadow-node),
@@ -183,3 +176,12 @@
183
176
  background: var(--hl-silver-hi);
184
177
  transform: scale(1.2);
185
178
  }
179
+
180
+ /* A node that has finished (RFC 0001): its inputs ended and its outputs
181
+ closed. It steps back, calmly, rather than going dark. */
182
+ .hl-pill {
183
+ transition: opacity 0.4s var(--hl-ease);
184
+ }
185
+ .hl-pill.is-finished {
186
+ opacity: 0.6;
187
+ }
@@ -4,6 +4,10 @@
4
4
  gap: 18px;
5
5
  padding: 14px;
6
6
  height: 100%;
7
+ min-height: 0;
8
+ /* Its own scroll region: a tall node (or a short pane) never clips it. */
9
+ overflow-y: auto;
10
+ overscroll-behavior: contain;
7
11
  }
8
12
 
9
13
  .hl-inspector-empty {
@@ -159,3 +163,58 @@
159
163
  font-weight: 600;
160
164
  color: var(--hl-ink-2);
161
165
  }
166
+
167
+ /* Run statistics of the selected node (NodeRunStats). */
168
+ .hl-runstats-hint {
169
+ text-align: left;
170
+ }
171
+ .hl-runstats {
172
+ display: grid;
173
+ grid-template-columns: repeat(3, minmax(0, 1fr));
174
+ gap: 6px;
175
+ margin: 0;
176
+ }
177
+ .hl-runstats-cell {
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: 3px;
181
+ min-width: 0;
182
+ padding: 7px 9px;
183
+ border-radius: var(--hl-r-sm);
184
+ background: rgba(255, 255, 255, 0.025);
185
+ box-shadow: var(--hl-glass-edge);
186
+ }
187
+ .hl-runstats-cell.is-wide {
188
+ grid-column: 1 / -1;
189
+ }
190
+ .hl-runstats-cell dt {
191
+ font-size: 10px;
192
+ color: var(--hl-ink-3);
193
+ }
194
+ .hl-runstats-cell dd {
195
+ margin: 0;
196
+ font-size: 12.5px;
197
+ color: var(--hl-ink);
198
+ overflow: hidden;
199
+ text-overflow: ellipsis;
200
+ white-space: nowrap;
201
+ }
202
+ .hl-runstats-cell.is-error dd {
203
+ color: var(--hl-error);
204
+ }
205
+
206
+ /* A connection in the inspector: its glyph, and the note above its form. */
207
+ .hl-inspector-kind.hl-connection {
208
+ --hl-kind: var(--hl-silver);
209
+ }
210
+ .hl-inspector-note {
211
+ text-align: left;
212
+ line-height: 1.5;
213
+ }
214
+
215
+ .hl-runstats-cell.is-warning dd {
216
+ color: var(--hl-warning);
217
+ }
218
+ .hl-runstats-cell.is-done dd {
219
+ color: var(--hl-ink-2);
220
+ }
@@ -0,0 +1,119 @@
1
+ /** PanelHead — a docked panel's header row; it responds to its own width. */
2
+
3
+ .hl-panel-head {
4
+ container: hl-panel-head / inline-size;
5
+ display: flex;
6
+ align-items: center;
7
+ gap: 12px;
8
+ box-sizing: border-box;
9
+ min-width: 0;
10
+ padding: 7px 12px 7px 16px;
11
+ border-bottom: 1px solid var(--hl-line);
12
+ flex: none;
13
+ }
14
+
15
+ .hl-panel-title {
16
+ font-size: 10px;
17
+ font-weight: 600;
18
+ text-transform: uppercase;
19
+ letter-spacing: 0.14em;
20
+ color: var(--hl-silver);
21
+ white-space: nowrap;
22
+ }
23
+
24
+ .hl-panel-meta {
25
+ min-width: 0;
26
+ overflow: hidden;
27
+ font-size: 11px;
28
+ color: var(--hl-ink-3);
29
+ white-space: nowrap;
30
+ text-overflow: ellipsis;
31
+ }
32
+
33
+ .hl-panel-actions {
34
+ display: flex;
35
+ gap: 4px;
36
+ margin-left: auto;
37
+ flex: none;
38
+ }
39
+
40
+ .hl-panel-action {
41
+ position: relative;
42
+ display: inline-flex;
43
+ align-items: center;
44
+ gap: 6px;
45
+ height: 26px;
46
+ max-width: 180px;
47
+ padding: 0 9px 0 7px;
48
+ border: 1px solid var(--hl-line);
49
+ border-radius: 6px;
50
+ background: transparent;
51
+ color: var(--hl-ink-3);
52
+ font-family: var(--hl-font-ui);
53
+ font-size: 11px;
54
+ font-weight: 550;
55
+ cursor: pointer;
56
+ transition: background 0.14s var(--hl-ease), color 0.14s var(--hl-ease);
57
+ }
58
+ .hl-panel-action:hover:not(:disabled) {
59
+ color: var(--hl-ink-2);
60
+ }
61
+ .hl-panel-action.is-on {
62
+ color: var(--hl-ink);
63
+ background: color-mix(in oklab, var(--hl-silver) 16%, transparent);
64
+ border-color: color-mix(in oklab, var(--hl-silver) 28%, transparent);
65
+ }
66
+ .hl-panel-action:disabled {
67
+ opacity: 0.45;
68
+ cursor: default;
69
+ }
70
+ .hl-panel-action:focus-visible {
71
+ outline: 1px solid var(--hl-silver);
72
+ outline-offset: -1px;
73
+ }
74
+ .hl-panel-action-icon {
75
+ display: grid;
76
+ flex: none;
77
+ place-items: center;
78
+ }
79
+ .hl-panel-action-label {
80
+ min-width: 0;
81
+ overflow: hidden;
82
+ white-space: nowrap;
83
+ text-overflow: ellipsis;
84
+ }
85
+
86
+ /* Narrow: icons only. The label stays in the accessibility tree. */
87
+ @container hl-panel-head (max-width: 599px) {
88
+ .hl-panel-action {
89
+ justify-content: center;
90
+ width: 30px;
91
+ padding: 0;
92
+ }
93
+ .hl-panel-action-label {
94
+ position: absolute;
95
+ width: 1px;
96
+ height: 1px;
97
+ overflow: hidden;
98
+ clip-path: inset(50%);
99
+ }
100
+ }
101
+
102
+ /* Narrower still: the status gives way to what the panel is and its actions. */
103
+ @container hl-panel-head (max-width: 419px) {
104
+ .hl-panel-meta {
105
+ display: none;
106
+ }
107
+ }
108
+
109
+ /* A finger needs a larger target than a pointer. */
110
+ @media (pointer: coarse) {
111
+ .hl-panel-action {
112
+ height: 30px;
113
+ }
114
+ @container hl-panel-head (max-width: 599px) {
115
+ .hl-panel-action {
116
+ width: 34px;
117
+ }
118
+ }
119
+ }
@@ -2,6 +2,11 @@
2
2
  display: flex;
3
3
  flex-direction: column;
4
4
  gap: 12px;
5
+ /* A fieldset, for its disabled state; without the browser's frame. */
6
+ min-width: 0;
7
+ margin: 0;
8
+ padding: 0;
9
+ border: 0;
5
10
  }
6
11
 
7
12
  .hl-field {
@@ -86,3 +91,14 @@
86
91
  border: 1px dashed var(--hl-line-strong);
87
92
  border-radius: var(--hl-r-sm);
88
93
  }
94
+
95
+ /* A field that does not apply yet (a connection's capacity while it is
96
+ unbounded): visibly off, and not interactive. */
97
+ .hl-input:disabled,
98
+ .hl-toggle:disabled {
99
+ opacity: 0.4;
100
+ cursor: not-allowed;
101
+ }
102
+ .hl-input:disabled:hover {
103
+ border-color: var(--hl-line-strong);
104
+ }
@@ -0,0 +1,119 @@
1
+ /** RunLog — the live console of a running graph, docked under the canvas. */
2
+
3
+ .hl-runlog {
4
+ container: hl-runlog / inline-size;
5
+ display: flex;
6
+ flex-direction: column;
7
+ min-height: 0;
8
+ height: 100%;
9
+ background: var(--hl-void-2);
10
+ border-top: 1px solid var(--hl-line);
11
+ }
12
+
13
+ .hl-runlog-hit:focus-visible {
14
+ outline: 1px solid var(--hl-silver);
15
+ outline-offset: -1px;
16
+ }
17
+
18
+ .hl-runlog-empty {
19
+ margin: 0;
20
+ padding: 14px 16px;
21
+ font-size: 12px;
22
+ color: var(--hl-ink-3);
23
+ }
24
+
25
+ .hl-runlog-body {
26
+ overscroll-behavior: contain;
27
+ list-style: none;
28
+ margin: 0;
29
+ padding: 4px 0;
30
+ overflow: auto;
31
+ min-height: 0;
32
+ flex: 1;
33
+ font-size: 11.5px;
34
+ }
35
+
36
+ .hl-runlog-hit {
37
+ display: grid;
38
+ grid-template-columns: 64px minmax(0, min(22rem, 40%)) minmax(0, 1fr) 72px;
39
+ align-items: baseline;
40
+ gap: 12px;
41
+ width: 100%;
42
+ padding: 2px 12px 2px 16px;
43
+ border: none;
44
+ background: transparent;
45
+ color: var(--hl-ink-2);
46
+ font: inherit;
47
+ text-align: left;
48
+ cursor: pointer;
49
+ }
50
+ .hl-runlog-hit:hover {
51
+ background: rgba(255, 255, 255, 0.035);
52
+ }
53
+
54
+ .hl-runlog-at {
55
+ color: var(--hl-ink-3);
56
+ font-variant-numeric: tabular-nums;
57
+ }
58
+ .hl-runlog-route {
59
+ display: flex;
60
+ align-items: baseline;
61
+ gap: 5px;
62
+ min-width: 0;
63
+ overflow: hidden;
64
+ white-space: nowrap;
65
+ }
66
+ .hl-runlog-node {
67
+ color: var(--hl-ink);
68
+ overflow: hidden;
69
+ text-overflow: ellipsis;
70
+ }
71
+ .hl-runlog-port {
72
+ color: var(--hl-ink-3);
73
+ flex: none;
74
+ }
75
+ .hl-runlog-arrow {
76
+ color: var(--hl-silver-lo);
77
+ flex: none;
78
+ }
79
+ .hl-runlog-value {
80
+ color: var(--hl-silver-hi);
81
+ overflow: hidden;
82
+ text-overflow: ellipsis;
83
+ white-space: nowrap;
84
+ }
85
+ .hl-runlog-ms {
86
+ color: var(--hl-ink-3);
87
+ text-align: right;
88
+ font-variant-numeric: tabular-nums;
89
+ white-space: nowrap;
90
+ }
91
+
92
+ .hl-runlog-row.is-error .hl-runlog-port,
93
+ .hl-runlog-row.is-error .hl-runlog-value {
94
+ color: var(--hl-error);
95
+ }
96
+ /* End of stream (RFC 0001): quiet, a lifecycle note rather than data. */
97
+ .hl-runlog-row.is-end .hl-runlog-value {
98
+ color: var(--hl-ink-3);
99
+ font-style: italic;
100
+ }
101
+ /* A message a drop-oldest connection let go of: lossy, in the warning hue. */
102
+ .hl-runlog-row.is-dropped .hl-runlog-value {
103
+ color: var(--hl-warning);
104
+ }
105
+
106
+ /* A narrow log puts the value on its own line. */
107
+ @container hl-runlog (max-width: 720px) {
108
+ .hl-runlog-hit {
109
+ grid-template-columns: 56px minmax(0, 1fr) 56px;
110
+ }
111
+ .hl-runlog-ms {
112
+ grid-column: 3;
113
+ grid-row: 1;
114
+ }
115
+ .hl-runlog-value {
116
+ grid-column: 2 / 4;
117
+ grid-row: 2;
118
+ }
119
+ }
@@ -80,3 +80,85 @@
80
80
  .hl-region-footer {
81
81
  flex-shrink: 0;
82
82
  }
83
+
84
+ /* A resizable footer: its height is set by `useFooterResize`, its content
85
+ fills it, and the grab handle straddles its top edge. */
86
+ .hl-region-footer.is-resizable {
87
+ position: relative;
88
+ display: flex;
89
+ flex-direction: column;
90
+ }
91
+ .hl-region-footer.is-resizable > :not(.hl-footer-resizer) {
92
+ flex: 1;
93
+ min-height: 0;
94
+ }
95
+ /* While dragging, nothing in the editor gets selected and the cursor stays. */
96
+ .hl-shell:has(.hl-region-footer.is-resizing) {
97
+ cursor: row-resize;
98
+ user-select: none;
99
+ -webkit-user-select: none;
100
+ }
101
+
102
+ .hl-footer-resizer {
103
+ position: absolute;
104
+ left: 0;
105
+ right: 0;
106
+ top: -6px;
107
+ height: 12px;
108
+ z-index: 6;
109
+ display: grid;
110
+ place-items: center;
111
+ cursor: row-resize;
112
+ /* The handle owns its drags: no scrolling or zooming of the page under it. */
113
+ touch-action: none;
114
+ outline: none;
115
+ }
116
+ .hl-footer-resizer::before {
117
+ /* The edge itself lights up while the handle is in use. */
118
+ content: '';
119
+ position: absolute;
120
+ left: 0;
121
+ right: 0;
122
+ top: 5px;
123
+ height: 2px;
124
+ background: transparent;
125
+ transition: background 0.14s var(--hl-ease);
126
+ }
127
+ .hl-footer-resizer::after {
128
+ /* The grip: a short silver bar in the middle of the edge. */
129
+ content: '';
130
+ width: 36px;
131
+ height: 4px;
132
+ border-radius: var(--hl-r-pill);
133
+ background: var(--hl-silver-lo);
134
+ opacity: 0;
135
+ transition: opacity 0.14s var(--hl-ease), background 0.14s var(--hl-ease);
136
+ }
137
+ .hl-footer-resizer:hover::after,
138
+ .hl-footer-resizer:focus-visible::after,
139
+ .hl-footer-resizer.is-dragging::after {
140
+ opacity: 1;
141
+ }
142
+ .hl-footer-resizer:hover::before,
143
+ .hl-footer-resizer:focus-visible::before,
144
+ .hl-footer-resizer.is-dragging::before {
145
+ background: color-mix(in oklab, var(--hl-silver) 45%, transparent);
146
+ }
147
+ .hl-footer-resizer.is-dragging::after {
148
+ background: var(--hl-silver-hi);
149
+ }
150
+
151
+ /* A finger needs a larger target, and a grip that shows without hovering. */
152
+ @media (pointer: coarse) {
153
+ .hl-footer-resizer {
154
+ top: -14px;
155
+ height: 28px;
156
+ }
157
+ .hl-footer-resizer::before {
158
+ top: 13px;
159
+ }
160
+ .hl-footer-resizer::after {
161
+ width: 44px;
162
+ opacity: 0.7;
163
+ }
164
+ }
package/lib/styles.css CHANGED
@@ -13,10 +13,12 @@
13
13
  @import './nodes/HlangNode.css';
14
14
  @import './nodes/AssetNode.css';
15
15
  @import './nodes/PillNode.css';
16
+ @import './live/LivePulse.css';
16
17
  @import './edges/FlowEdge.css';
17
18
  @import './shell/EditorShell.css';
18
19
  @import './toolbar/Toolbar.css';
19
20
  @import './toolbar/ToolCluster.css';
21
+ @import './toolbar/ReadOnlyBadge.css';
20
22
  @import './canvas/CanvasChrome.css';
21
23
  @import './canvas/CanvasDock.css';
22
24
  @import './canvas/EmptyState.css';
@@ -27,6 +29,8 @@
27
29
  @import './panels/RailTabs.css';
28
30
  @import './panels/AssetGrid.css';
29
31
  @import './panels/PositionPanel.css';
32
+ @import './panels/PanelHead.css';
33
+ @import './panels/RunLog.css';
30
34
  @import './command-palette/NodeSearch.css';
31
35
  @import './context-menu/NodeContextMenu.css';
32
36
  @import './overlays/Cheatsheet.css';
@@ -0,0 +1,35 @@
1
+ /** ReadOnlyBadge — a quiet pill in the toolbar: the graph is view only. */
2
+
3
+ .hl-readonly-badge {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ gap: 6px;
7
+ flex: none;
8
+ padding: 4px 10px 4px 8px;
9
+ border: 1px solid var(--hl-line);
10
+ border-radius: var(--hl-r-pill);
11
+ background: rgba(255, 255, 255, 0.04);
12
+ color: var(--hl-ink-2);
13
+ font-family: var(--hl-font-ui);
14
+ font-size: 11.5px;
15
+ font-weight: 550;
16
+ white-space: nowrap;
17
+ }
18
+ .hl-readonly-badge svg {
19
+ flex: none;
20
+ color: var(--hl-silver);
21
+ }
22
+
23
+ @container hl-toolbar (max-width: 419px) {
24
+ .hl-readonly-badge {
25
+ padding: 6px;
26
+ }
27
+ .hl-readonly-badge-label {
28
+ position: absolute;
29
+ width: 1px;
30
+ height: 1px;
31
+ overflow: hidden;
32
+ clip-path: inset(50%);
33
+ white-space: nowrap;
34
+ }
35
+ }
@@ -1,10 +1,13 @@
1
1
  /** Frosted-glass toolbar with a wordmark and a right-aligned action cluster. */
2
2
 
3
3
  .hl-toolbar {
4
+ /* Its children respond to the toolbar's width (e.g. ReadOnlyBadge). */
5
+ container: hl-toolbar / inline-size;
4
6
  position: relative;
5
7
  display: flex;
6
8
  align-items: center;
7
9
  justify-content: space-between;
10
+ gap: 12px;
8
11
  height: 54px;
9
12
  padding: 0 16px;
10
13
  border-bottom: 1px solid var(--hl-line);
@@ -27,6 +30,9 @@
27
30
  display: flex;
28
31
  align-items: center;
29
32
  gap: 10px;
33
+ /* The brand gives way first: the actions are what the toolbar is for. */
34
+ min-width: 0;
35
+ overflow: hidden;
30
36
  }
31
37
  .hl-toolbar-glyph {
32
38
  width: 14px;
@@ -36,6 +42,10 @@
36
42
  background: radial-gradient(circle at 30% 25%, var(--hl-silver-hi), var(--hl-silver-lo) 70%);
37
43
  box-shadow: 0 0 10px -3px var(--hl-silver), var(--hl-glass-edge-hi);
38
44
  }
45
+ .hl-toolbar-glyph,
46
+ .hl-toolbar-wordmark {
47
+ flex: none;
48
+ }
39
49
  .hl-toolbar-wordmark {
40
50
  font-size: 15px;
41
51
  font-weight: 600;
@@ -47,6 +57,7 @@
47
57
  display: flex;
48
58
  align-items: center;
49
59
  gap: 8px;
60
+ flex: none;
50
61
  }
51
62
 
52
63
  /* Read-only stat pill (node/vertex count). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hlang-tech/editor-ui",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Lumen design system for hlang: node, edge, panel, and shell components for a React Flow node editor, built on @hlang-org/editor-react.",
5
5
  "keywords": [
6
6
  "hlang",
@@ -59,9 +59,9 @@
59
59
  "access": "public"
60
60
  },
61
61
  "dependencies": {
62
- "@hlang-tech/editor-react": "^0.1.2",
63
- "@hlang-tech/interpreter": "^2.0.0",
64
- "@hlang-tech/editor-core": "^0.2.0"
62
+ "@hlang-tech/editor-core": "^0.4.0",
63
+ "@hlang-tech/editor-react": "^0.3.0",
64
+ "@hlang-tech/interpreter": "^2.1.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@xyflow/react": "^12.0.0",