@w6w/ui 0.0.0-stage → 0.1.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.
Files changed (143) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +260 -2
  4. package/dist/ActionTestForm.d.ts +76 -0
  5. package/dist/AddConnectionModal.d.ts +24 -0
  6. package/dist/AppPicker.d.ts +83 -0
  7. package/dist/CodeBlock-CZu4TFSi.cjs +15 -0
  8. package/dist/CodeBlock-CZu4TFSi.cjs.map +1 -0
  9. package/dist/CodeBlock-Dot3Pd7P.js +2889 -0
  10. package/dist/CodeBlock-Dot3Pd7P.js.map +1 -0
  11. package/dist/CodeBlock.d.ts +65 -0
  12. package/dist/CodeEditor.d.ts +42 -0
  13. package/dist/ConfirmModal-DJPYXE9t.cjs +11 -0
  14. package/dist/ConfirmModal-DJPYXE9t.cjs.map +1 -0
  15. package/dist/ConfirmModal-sY1tzkBO.js +6335 -0
  16. package/dist/ConfirmModal-sY1tzkBO.js.map +1 -0
  17. package/dist/Copyable-1niV9gUa.js +771 -0
  18. package/dist/Copyable-1niV9gUa.js.map +1 -0
  19. package/dist/Copyable-CYZU1o4X.cjs +2 -0
  20. package/dist/Copyable-CYZU1o4X.cjs.map +1 -0
  21. package/dist/JsonEditor.d.ts +60 -0
  22. package/dist/NodeConfigForm.d.ts +70 -0
  23. package/dist/ParamsForm.d.ts +56 -0
  24. package/dist/PropertyEntryForm.d.ts +56 -0
  25. package/dist/RetryPolicyFields.d.ts +18 -0
  26. package/dist/StepBuilderModal.d.ts +375 -0
  27. package/dist/TriggerFillForm.d.ts +52 -0
  28. package/dist/WaitEditor.d.ts +6 -0
  29. package/dist/WorkflowFlowEditor.d.ts +119 -0
  30. package/dist/YamlEditor.d.ts +46 -0
  31. package/dist/app-categories.d.ts +38 -0
  32. package/dist/app-pages.d.ts +111 -0
  33. package/dist/code.cjs +2 -0
  34. package/dist/code.cjs.map +1 -0
  35. package/dist/code.d.ts +29 -0
  36. package/dist/code.mjs +7 -0
  37. package/dist/code.mjs.map +1 -0
  38. package/dist/components/ApiCallsPanel.d.ts +41 -0
  39. package/dist/components/AppIcon.d.ts +25 -0
  40. package/dist/components/AuthFieldsForm.d.ts +22 -0
  41. package/dist/components/Combobox.d.ts +24 -0
  42. package/dist/components/ConfirmModal.d.ts +33 -0
  43. package/dist/components/Copyable.d.ts +51 -0
  44. package/dist/components/CopyableText.d.ts +40 -0
  45. package/dist/components/DeleteButton.d.ts +16 -0
  46. package/dist/components/EditButton.d.ts +10 -0
  47. package/dist/components/ExecutionDetail.d.ts +70 -0
  48. package/dist/components/ExecutionFilters.d.ts +59 -0
  49. package/dist/components/ExecutionList.d.ts +60 -0
  50. package/dist/components/ExecutionLogPanel.d.ts +98 -0
  51. package/dist/components/ExecutionStats.d.ts +47 -0
  52. package/dist/components/ExpressionEditorModal.d.ts +42 -0
  53. package/dist/components/ExpressionInput.d.ts +36 -0
  54. package/dist/components/ExpressionOptions.d.ts +185 -0
  55. package/dist/components/HealthStatusPill.d.ts +25 -0
  56. package/dist/components/HistoryTimeline.d.ts +56 -0
  57. package/dist/components/Icon.d.ts +34 -0
  58. package/dist/components/IconButton.d.ts +60 -0
  59. package/dist/components/InfoTip.d.ts +15 -0
  60. package/dist/components/InternalIcon.d.ts +19 -0
  61. package/dist/components/ListItem.d.ts +25 -0
  62. package/dist/components/Modal.d.ts +29 -0
  63. package/dist/components/RepoSyncIndicator.d.ts +29 -0
  64. package/dist/components/ResolvedParams.d.ts +31 -0
  65. package/dist/components/ServerResources.d.ts +47 -0
  66. package/dist/components/StepStatusPill.d.ts +51 -0
  67. package/dist/components/UptimeStrip.d.ts +56 -0
  68. package/dist/components/execution-format.d.ts +24 -0
  69. package/dist/components/expression-dom.d.ts +154 -0
  70. package/dist/components/expression-template.d.ts +86 -0
  71. package/dist/components/history-scale.d.ts +33 -0
  72. package/dist/components/icons.d.ts +41 -0
  73. package/dist/components/resolved-clip.d.ts +42 -0
  74. package/dist/components/server-resources-format.d.ts +68 -0
  75. package/dist/components/server-resources.fixture.d.ts +9 -0
  76. package/dist/components/use-copy.d.ts +14 -0
  77. package/dist/createW6WApi.d.ts +31 -0
  78. package/dist/flow-connect.d.ts +138 -0
  79. package/dist/flow-fanout.d.ts +15 -0
  80. package/dist/flow-types.d.ts +356 -0
  81. package/dist/flow-utils.d.ts +284 -0
  82. package/dist/flow.cjs +3 -0
  83. package/dist/flow.cjs.map +1 -0
  84. package/dist/flow.d.ts +22 -0
  85. package/dist/flow.mjs +1977 -0
  86. package/dist/flow.mjs.map +1 -0
  87. package/dist/index.cjs +2 -0
  88. package/dist/index.cjs.map +1 -0
  89. package/dist/index.d.ts +94 -0
  90. package/dist/index.mjs +1811 -0
  91. package/dist/index.mjs.map +1 -0
  92. package/dist/oauth-popup.d.ts +17 -0
  93. package/dist/output-shape.d.ts +58 -0
  94. package/dist/provider.d.ts +420 -0
  95. package/dist/resolve-params.d.ts +112 -0
  96. package/dist/resolved-json.d.ts +21 -0
  97. package/dist/run-visuals.d.ts +90 -0
  98. package/dist/step-preview-state.d.ts +143 -0
  99. package/dist/theme.d.ts +32 -0
  100. package/dist/trigger-fields.d.ts +56 -0
  101. package/dist/types.d.ts +413 -0
  102. package/dist/ui.css +1 -0
  103. package/dist/use-app-action-defs.d.ts +7 -0
  104. package/dist/use-enter-submit.d.ts +23 -0
  105. package/dist/use-seed-sources.d.ts +16 -0
  106. package/package.json +111 -3
  107. package/src/code.css +542 -0
  108. package/src/styles/_buttons.scss +38 -0
  109. package/src/styles/_chips.scss +210 -0
  110. package/src/styles/_code.scss +139 -0
  111. package/src/styles/_copyable.scss +147 -0
  112. package/src/styles/_data-vars.scss +38 -0
  113. package/src/styles/_execution-history.scss +282 -0
  114. package/src/styles/_execution-log.scss +178 -0
  115. package/src/styles/_expression-field.scss +65 -0
  116. package/src/styles/_expression-inline.scss +63 -0
  117. package/src/styles/_expression-modal.scss +292 -0
  118. package/src/styles/_fields.scss +161 -0
  119. package/src/styles/_flow.scss +217 -0
  120. package/src/styles/_fx.scss +158 -0
  121. package/src/styles/_health.scss +46 -0
  122. package/src/styles/_history-timeline.scss +156 -0
  123. package/src/styles/_icon-button.scss +22 -0
  124. package/src/styles/_icon.scss +15 -0
  125. package/src/styles/_icons.scss +31 -0
  126. package/src/styles/_incoming-state.scss +46 -0
  127. package/src/styles/_info-tip.scss +9 -0
  128. package/src/styles/_layout.scss +6 -0
  129. package/src/styles/_lists.scss +64 -0
  130. package/src/styles/_modal.scss +125 -0
  131. package/src/styles/_multiselect.scss +69 -0
  132. package/src/styles/_repo-sync-indicator.scss +99 -0
  133. package/src/styles/_scale.scss +70 -0
  134. package/src/styles/_sections.scss +36 -0
  135. package/src/styles/_server-resources.scss +163 -0
  136. package/src/styles/_step-builder.scss +376 -0
  137. package/src/styles/_step-config.scss +195 -0
  138. package/src/styles/_tabs.scss +54 -0
  139. package/src/styles/_tester.scss +111 -0
  140. package/src/styles/_tokens.scss +141 -0
  141. package/src/styles/_uptime.scss +61 -0
  142. package/src/styles.css +4208 -0
  143. package/src/styles.scss +61 -0
package/src/code.css ADDED
@@ -0,0 +1,542 @@
1
+ /* GENERATED FILE — DO NOT EDIT.
2
+ *
3
+ * Compiled from src/code.scss by scripts/build-css.mjs (`pnpm build:css`).
4
+ * Edit the .scss partials under src/styles/ instead; `pnpm check:css` fails
5
+ * if this file has drifted from them.
6
+ */
7
+ @charset "UTF-8";
8
+ /*
9
+ * @w6w/ui — styles for the pure-presentation components.
10
+ *
11
+ * Everything is namespaced under `.w6w-*` class names so this stylesheet
12
+ * won't collide with your app's CSS.
13
+ *
14
+ * THEMING
15
+ * -------
16
+ * The library ships a complete DEFAULT theme in BOTH light and dark:
17
+ * - Light is the base.
18
+ * - Dark applies automatically when the OS prefers dark
19
+ * (`prefers-color-scheme: dark`).
20
+ * - Either mode can be forced with `data-theme="light" | "dark"` on any
21
+ * ancestor (usually <html>) — the same signal the JS components read
22
+ * (AppIcon / CodeEditor / JsonEditor: explicit prop > data-theme > OS pref),
23
+ * so CSS and components always agree.
24
+ *
25
+ * A HOST can override any subset of the tokens — set the ones you care about and
26
+ * the rest keep their themed defaults:
27
+ *
28
+ * :root { --w6w-accent: #3355e6; } // one token, both modes
29
+ * html[data-theme="dark"] { --w6w-panel: #000 } // mode-specific
30
+ *
31
+ * Every default below is declared through `:where(...)` (specificity 0), so ANY
32
+ * host rule — even a plain `:root { ... }` — wins without !important. Tokens are
33
+ * defined on :root so every `.w6w-*` component inherits them out of the box.
34
+ */
35
+ /* Light — the base defaults. */
36
+ :where(:root) {
37
+ color-scheme: light;
38
+ --w6w-bg: #f7f8fa;
39
+ --w6w-panel: #ffffff;
40
+ --w6w-panel-2: #f0f2f6;
41
+ --w6w-border: #d9dee5;
42
+ --w6w-text: #1a1d24;
43
+ --w6w-muted: #5f6875;
44
+ --w6w-accent: #3355e6;
45
+ --w6w-danger: #c1362f;
46
+ --w6w-success: #2e9e5b;
47
+ --w6w-health-ok: var(--w6w-success);
48
+ --w6w-health-degraded: #c77700;
49
+ --w6w-health-down: var(--w6w-danger);
50
+ --w6w-health-unknown: #5f6875;
51
+ --w6w-icon-swatch: #f0f2f6;
52
+ --w6w-code-comment: #6b7280;
53
+ --w6w-code-keyword: #8250df;
54
+ --w6w-code-string: #0a7b34;
55
+ --w6w-code-number: #b3541e;
56
+ --w6w-code-function: #1f6feb;
57
+ --w6w-code-punctuation: #5f6875;
58
+ --w6w-code-variable: #953800;
59
+ --w6w-radius: 10px;
60
+ }
61
+
62
+ /* Dark — followed automatically from the OS preference (unless a host has
63
+ forced a mode via data-theme, which is handled by the blocks below). */
64
+ @media (prefers-color-scheme: dark) {
65
+ :where(:root) {
66
+ color-scheme: dark;
67
+ --w6w-bg: #0f1115;
68
+ --w6w-panel: #181b22;
69
+ --w6w-panel-2: #1f232c;
70
+ --w6w-border: #2a2f3a;
71
+ --w6w-text: #e6e8ee;
72
+ --w6w-muted: #9aa3b2;
73
+ --w6w-accent: #5b8cff;
74
+ --w6w-danger: #ff6b6b;
75
+ --w6w-success: #3fbf77;
76
+ --w6w-health-ok: var(--w6w-success);
77
+ --w6w-health-degraded: #f0a020;
78
+ --w6w-health-down: var(--w6w-danger);
79
+ --w6w-health-unknown: #9aa3b2;
80
+ --w6w-icon-swatch: #1f232c;
81
+ --w6w-code-comment: #7d8797;
82
+ --w6w-code-keyword: #d2a8ff;
83
+ --w6w-code-string: #7ee787;
84
+ --w6w-code-number: #ffab70;
85
+ --w6w-code-function: #79c0ff;
86
+ --w6w-code-punctuation: #9aa3b2;
87
+ --w6w-code-variable: #ffa657;
88
+ }
89
+ }
90
+ /* Explicit mode. Declared AFTER the media query at equal (zero) specificity, so
91
+ a forced data-theme always beats the OS preference. */
92
+ :where([data-theme=light]) {
93
+ color-scheme: light;
94
+ --w6w-bg: #f7f8fa;
95
+ --w6w-panel: #ffffff;
96
+ --w6w-panel-2: #f0f2f6;
97
+ --w6w-border: #d9dee5;
98
+ --w6w-text: #1a1d24;
99
+ --w6w-muted: #5f6875;
100
+ --w6w-accent: #3355e6;
101
+ --w6w-danger: #c1362f;
102
+ --w6w-success: #2e9e5b;
103
+ --w6w-health-ok: var(--w6w-success);
104
+ --w6w-health-degraded: #c77700;
105
+ --w6w-health-down: var(--w6w-danger);
106
+ --w6w-health-unknown: #5f6875;
107
+ --w6w-icon-swatch: #f0f2f6;
108
+ --w6w-code-comment: #6b7280;
109
+ --w6w-code-keyword: #8250df;
110
+ --w6w-code-string: #0a7b34;
111
+ --w6w-code-number: #b3541e;
112
+ --w6w-code-function: #1f6feb;
113
+ --w6w-code-punctuation: #5f6875;
114
+ --w6w-code-variable: #953800;
115
+ }
116
+
117
+ :where([data-theme=dark]) {
118
+ color-scheme: dark;
119
+ --w6w-bg: #0f1115;
120
+ --w6w-panel: #181b22;
121
+ --w6w-panel-2: #1f232c;
122
+ --w6w-border: #2a2f3a;
123
+ --w6w-text: #e6e8ee;
124
+ --w6w-muted: #9aa3b2;
125
+ --w6w-accent: #5b8cff;
126
+ --w6w-danger: #ff6b6b;
127
+ --w6w-success: #3fbf77;
128
+ --w6w-health-ok: var(--w6w-success);
129
+ --w6w-health-degraded: #f0a020;
130
+ --w6w-health-down: var(--w6w-danger);
131
+ --w6w-health-unknown: #9aa3b2;
132
+ --w6w-icon-swatch: #1f232c;
133
+ --w6w-code-comment: #7d8797;
134
+ --w6w-code-keyword: #d2a8ff;
135
+ --w6w-code-string: #7ee787;
136
+ --w6w-code-number: #ffab70;
137
+ --w6w-code-function: #79c0ff;
138
+ --w6w-code-punctuation: #9aa3b2;
139
+ --w6w-code-variable: #ffa657;
140
+ }
141
+
142
+ .w6w-muted {
143
+ color: var(--w6w-muted);
144
+ }
145
+
146
+ .w6w-small {
147
+ font-size: 0.85em;
148
+ }
149
+
150
+ /*
151
+ * @w6w/ui — the spacing / typography scale.
152
+ *
153
+ * Values are MIRRORED, not invented, from `packages/frontend/packages/web`'s
154
+ * `src/styles/global.css:83-130` — that file is the canonical reference (it
155
+ * arrived at the same 4px base independently); this block copies its
156
+ * `--sp-*`/`--fs-*`/`--fw-*`/`--lh-*`/`--font-*` values byte-for-byte under the
157
+ * `--w6w-*` namespace this package already uses for every other token.
158
+ * `--fs-hero`'s `clamp()` stays `web`-only (a marketing-page concern; `ui` has
159
+ * no hero). Colour tokens, `--radius*`, `--shadow-*` and `--transition-fast`
160
+ * are NOT part of this scale — see `_tokens.scss`.
161
+ *
162
+ * HALF-STEPS (`--w6w-sp-0-5` / `--w6w-sp-1-5` / `--w6w-sp-2-5`) are `ui`-only
163
+ * additions on top of `web`'s set — `web` has none. The rule that keeps this a
164
+ * system rather than a free-for-all: whole steps for new code; a half-step is
165
+ * only for preserving an existing measured value that does not sit on a 4px
166
+ * step. Parity with `web` is therefore DIRECTIONAL — `ui` ⊇ `web` on the
167
+ * shared key set (every `web` token here has a byte-identical `ui` counterpart;
168
+ * `ui` additionally declares the three half-steps `web` does not).
169
+ *
170
+ * One `:where(:root)` block, not colour's three-way light / prefers-color-scheme /
171
+ * [data-theme] split — the scale is not theme-dependent, so a single specificity-0
172
+ * block covers every mode. Positioning this partial's `@use` line immediately
173
+ * after `styles/tokens` (see `styles.scss`) is a readability convention, not a
174
+ * cascade constraint: CSS custom properties resolve at computed-value time, so
175
+ * definition order relative to consumption order does not matter here, unlike
176
+ * `_tokens.scss`'s internal prefers-color-scheme / [data-theme] ordering, which
177
+ * IS load-bearing.
178
+ */
179
+ :where(:root) {
180
+ /* Spacing — 4px ramp. Values mirror frontend/web's --sp-* verbatim. */
181
+ --w6w-sp-1: 4px;
182
+ --w6w-sp-2: 8px;
183
+ --w6w-sp-3: 12px;
184
+ --w6w-sp-4: 16px;
185
+ --w6w-sp-5: 20px;
186
+ --w6w-sp-6: 24px;
187
+ --w6w-sp-8: 32px;
188
+ --w6w-sp-10: 40px;
189
+ --w6w-sp-12: 48px;
190
+ --w6w-sp-16: 64px;
191
+ --w6w-sp-20: 80px;
192
+ /* Half-steps — ui-only, see the decision above. */
193
+ --w6w-sp-0-5: 2px;
194
+ --w6w-sp-1-5: 6px;
195
+ --w6w-sp-2-5: 10px;
196
+ /* Type ramp — rem, role names mirror frontend/web's --fs-*. */
197
+ --w6w-fs-xs: 0.75rem;
198
+ --w6w-fs-sm: 0.875rem;
199
+ --w6w-fs-base: 1rem;
200
+ --w6w-fs-lg: 1.125rem;
201
+ --w6w-fs-xl: 1.25rem;
202
+ --w6w-fs-2xl: 1.5rem;
203
+ --w6w-fs-3xl: 2rem;
204
+ --w6w-fw-regular: 400;
205
+ --w6w-fw-medium: 500;
206
+ --w6w-fw-semibold: 600;
207
+ --w6w-fw-bold: 700;
208
+ --w6w-lh-tight: 1.2;
209
+ --w6w-lh-normal: 1.5;
210
+ --w6w-lh-relaxed: 1.7;
211
+ --w6w-font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
212
+ --w6w-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
213
+ }
214
+
215
+ /*
216
+ * Breakpoints — Sass variables, NOT CSS custom properties.
217
+ *
218
+ * `@media (min-width: var(--w6w-bp-md))` is invalid in every browser: a media
219
+ * query condition cannot read a custom property. So these live as Sass
220
+ * variables instead, resolved before the browser ever sees the stylesheet.
221
+ * `!default` lets a Sass consumer override one:
222
+ *
223
+ * @use "@w6w/ui/styles/scale" with ($w6w-bp-md: 960px);
224
+ *
225
+ * `ui` itself consumes none of these — it deliberately ships no layout media
226
+ * queries (the host owns page layout; `ui` owns component internals). They
227
+ * exist purely so a consumer stops inventing its own breakpoint values (seven
228
+ * uncoordinated ones exist across the four repos today).
229
+ */
230
+ /* Copyable — an in-box copy affordance decorating a value-displaying control
231
+ (an <input>, a <textarea>, or CodeBlock's <pre>). Mirrors `.w6w-fx-wrap`
232
+ (`_fx.scss:1-88`): the WRAPPER owns the box chrome and every child it holds
233
+ is flattened below, so the value and the copy affordance read as ONE box. */
234
+ .w6w-copyable {
235
+ display: flex;
236
+ align-items: center;
237
+ gap: var(--w6w-sp-1);
238
+ background: var(--w6w-panel-2);
239
+ border: 1px solid var(--w6w-border);
240
+ border-radius: 8px;
241
+ padding: var(--w6w-sp-1) var(--w6w-sp-2);
242
+ /* Box-family invariant: byte-identical to `.w6w-fx-wrap`'s at `_fx.scss:13` —
243
+ six rules across four partials share this value (`building-blocks.md` §3
244
+ row 7); a scale token here would drift the moment that family moves. */
245
+ min-height: 38px;
246
+ }
247
+
248
+ /* A textarea/pre is taller than the button, so the button rides at the top —
249
+ mirrors `.w6w-fx-wrap:has(> textarea)` (`_fx.scss:16-18`). */
250
+ .w6w-copyable:has(> textarea),
251
+ .w6w-copyable:has(> pre) {
252
+ align-items: flex-start;
253
+ }
254
+
255
+ /* Focus reads on the box now that the inner control has no border of its own. */
256
+ .w6w-copyable:focus-within {
257
+ border-color: var(--w6w-accent);
258
+ }
259
+
260
+ .w6w-copyable > input,
261
+ .w6w-copyable > textarea,
262
+ .w6w-copyable > pre {
263
+ flex: 1 1 auto;
264
+ min-width: 0;
265
+ }
266
+
267
+ /* The control inside the wrapper carries NO chrome of its own. Mirrors
268
+ `_fx.scss:36-88`'s selector list and property list WHOLESALE
269
+ (s/w6w-fx-wrap/w6w-copyable/g) — not re-derived, gated for parity by the
270
+ project's test plan. See that file for the measured leak evidence (7.0px
271
+ alignment / 2px / 29% height) each property below defends against; a host
272
+ page's unscoped `input[type="text"], … textarea { … }` rule (studio's own,
273
+ layered AFTER this stylesheet) is exactly what these declarations beat. */
274
+ .w6w-field .w6w-copyable input[type=text],
275
+ .w6w-field .w6w-copyable input[type=password],
276
+ .w6w-field .w6w-copyable input[type=number],
277
+ .w6w-field .w6w-copyable select,
278
+ .w6w-field .w6w-copyable textarea,
279
+ .w6w-datavar-row .w6w-copyable input,
280
+ .w6w-datavar-row .w6w-copyable select,
281
+ .w6w-copyable input[type=text],
282
+ .w6w-copyable input[type=password],
283
+ .w6w-copyable input[type=number],
284
+ .w6w-copyable select,
285
+ .w6w-copyable textarea {
286
+ border: none;
287
+ background: transparent;
288
+ border-radius: 0;
289
+ /* lint-tokens-allow: mirrors _fx.scss:36-88 byte-for-byte, required by the parity gate */
290
+ padding: 4px 2px;
291
+ outline: none;
292
+ margin: 0;
293
+ min-height: 0;
294
+ }
295
+
296
+ /* The wrapper never draws a box around a box (mirrors `_fx.scss:91-98`). */
297
+ .w6w-copyable .w6w-code-block {
298
+ border: none;
299
+ background: transparent;
300
+ padding: 0;
301
+ }
302
+
303
+ /* Button chrome extends `.w6w-icon-btn` (`_icons.scss:1-11`) in markup — this
304
+ class only pins the flex behaviour and the copied state, no re-authored
305
+ hover/active states. */
306
+ .w6w-copyable-btn {
307
+ flex: none;
308
+ }
309
+
310
+ /* ── CodeBlock: the button OVERLAYS the box's top-right corner ──────────────
311
+ For an <input>/<textarea> the button is a flex sibling, which is right: the
312
+ value is one line and the control shrinks to leave room for it. A <pre> is
313
+ different — it is a block of many lines, and a flex sibling would carve a
314
+ permanent empty column out of the full height of it to seat one 28px button.
315
+ So here the button leaves the flow and sits over the corner instead.
316
+
317
+ Absolute against the WRAPPER, not the <pre>, on purpose: the <pre> is the
318
+ element that scrolls (`overflow-x: auto`, plus `overflow-y` when `maxHeight`
319
+ is set), so anchoring to it would slide the button out of view on a wide or
320
+ tall snippet. Anchored to the wrapper it stays pinned to the visible corner. */
321
+ .w6w-copyable:has(> pre) {
322
+ position: relative;
323
+ }
324
+
325
+ .w6w-copyable:has(> pre) > .w6w-copyable-btn {
326
+ position: absolute;
327
+ top: var(--w6w-sp-1);
328
+ right: var(--w6w-sp-1);
329
+ /* An opaque backdrop, which the flex-sibling case does not need: this button
330
+ sits ON the code, and `.w6w-icon-btn`'s transparent idle state would let
331
+ the first line's text read through it. It also restores the hover feedback
332
+ — `.w6w-icon-btn:hover` paints `--w6w-panel-2`, which is the wrapper's own
333
+ background here and would otherwise be a no-op. */
334
+ background: var(--w6w-panel);
335
+ border-color: var(--w6w-border);
336
+ }
337
+
338
+ /* Keep the first line clear of the button. Deliberately NOT folded into the
339
+ `.w6w-copyable .w6w-code-block` reset above — that rule mirrors
340
+ `_fx.scss:91-98` and is held byte-for-byte by the parity gate. 40px = the
341
+ button's ~28px box + its 4px inset + a 8px gap before the text. */
342
+ .w6w-copyable:has(> pre) > .w6w-code-block {
343
+ padding-right: var(--w6w-sp-10);
344
+ }
345
+
346
+ .w6w-copyable-btn.is-copied {
347
+ /* Mirrors `.w6w-expr-chip-render`'s "confirmed/success" convention exactly
348
+ (`_chips.scss:38-42`) — the one visual for a copied/success state. */
349
+ border-color: var(--w6w-success);
350
+ color: var(--w6w-success);
351
+ background: color-mix(in srgb, var(--w6w-success) 14%, transparent);
352
+ }
353
+
354
+ /* Scoped visually-hidden live region — not a new general `sr-only` utility
355
+ (none exists in `ui` to extend; see `Copyable.tsx` for why this, not the
356
+ button's own accessible name, carries the "Copied" announcement). */
357
+ .w6w-copyable-status {
358
+ position: absolute;
359
+ overflow: hidden;
360
+ width: 1px;
361
+ height: 1px;
362
+ white-space: nowrap;
363
+ clip-path: inset(50%);
364
+ border: 0;
365
+ padding: 0;
366
+ }
367
+
368
+ /* ── Bare modifier: CopyableText (D-7) ───────────────────────────────────
369
+ A self-contained modifier, NOT a widening of the shared `> input, >
370
+ textarea, > pre` selector above (`:28-33`) — that list stays untouched.
371
+ Strips the box chrome the base `.w6w-copyable` rule sets (`:5-17`: border,
372
+ background, padding, min-height), and separately gives its own `> span`
373
+ child the SAME flex-and-shrink pair the shared selector gives an
374
+ `<input>`/`<textarea>`/`<pre>` (`:28-33` below) — a bare `<span>` is not in
375
+ that list, and without this it silently reproduces the overflow bug
376
+ `studio/src/pages/VarsPage.tsx:62-79` had to hand-patch. */
377
+ .w6w-copyable--bare {
378
+ border: none;
379
+ background: transparent;
380
+ padding: 0;
381
+ min-height: 0;
382
+ }
383
+
384
+ .w6w-copyable--bare > span {
385
+ flex: 1 1 auto;
386
+ min-width: 0;
387
+ }
388
+
389
+ .w6w-code-block {
390
+ background: var(--w6w-panel-2);
391
+ border: 1px solid var(--w6w-border);
392
+ border-radius: var(--w6w-radius);
393
+ margin: 0;
394
+ overflow-x: auto;
395
+ padding: var(--w6w-sp-3) 14px;
396
+ tab-size: 2;
397
+ }
398
+
399
+ .w6w-code-block code {
400
+ background: none;
401
+ display: block;
402
+ font-family: var(--w6w-font-mono);
403
+ font-size: 13px;
404
+ line-height: 1.55;
405
+ padding: 0;
406
+ }
407
+
408
+ /* Each line is its own row so a line number can sit beside the text rather
409
+ than inside it — a number inside the flow would be caught by a copy. */
410
+ .w6w-code-line {
411
+ display: flex;
412
+ gap: var(--w6w-sp-3);
413
+ white-space: pre;
414
+ }
415
+
416
+ .w6w-code-line-text {
417
+ white-space: inherit;
418
+ }
419
+
420
+ /* `user-select: none` keeps the gutter out of a mouse selection; `aria-hidden`
421
+ on the element keeps it out of the accessibility tree. Both are needed —
422
+ neither one alone stops a copy from picking up the digits. */
423
+ .w6w-code-line-no {
424
+ color: var(--w6w-muted);
425
+ flex: 0 0 auto;
426
+ min-width: 2ch;
427
+ opacity: 0.65;
428
+ text-align: right;
429
+ user-select: none;
430
+ }
431
+
432
+ /* Wrapped mode indents continuation rows to the text column so a wrapped line
433
+ still reads as one line. */
434
+ .w6w-code-block--wrap .w6w-code-line,
435
+ .w6w-code-block--wrap .w6w-code-line-text {
436
+ white-space: pre-wrap;
437
+ word-break: break-word;
438
+ }
439
+
440
+ .w6w-code-block--wrap {
441
+ overflow-x: hidden;
442
+ }
443
+
444
+ /* Resolved params (Test tab, read-only): inline badge weight — like
445
+ JsonParamField's "Invalid JSON" hint, not a full `.w6w-result.w6w-error`
446
+ block per param, since either can appear once per configured param. */
447
+ .w6w-param-unresolved {
448
+ color: var(--w6w-danger);
449
+ font-size: 0.85em;
450
+ font-style: italic;
451
+ }
452
+
453
+ .w6w-param-unevaluated {
454
+ color: var(--w6w-muted);
455
+ font-size: 0.85em;
456
+ font-style: italic;
457
+ }
458
+
459
+ /* One resolved param: a table row, not a stacked form field — this list is
460
+ read-only display, never an editable `.w6w-field`, so it earns its own
461
+ compact key-left/value-right layout instead of a label-above-control block
462
+ (human report, 2026-09-01: key and value were indistinguishable and every
463
+ row burned a full stacked block of vertical space). ONE grid across the
464
+ whole list (`.w6w-resolved-table`), not one grid per row, so every row's
465
+ key column lines up with the one above and below it. */
466
+ .w6w-resolved-table {
467
+ display: grid;
468
+ grid-template-columns: auto 1fr;
469
+ align-items: baseline;
470
+ }
471
+
472
+ /* Each row is `display: contents` — it renders no box of its own, so its two
473
+ children (label, value) become direct items of the parent grid above,
474
+ sharing its column tracks instead of sizing their own. */
475
+ .w6w-resolved-row {
476
+ display: contents;
477
+ font-size: calc(var(--w6w-fs-sm) * 0.9);
478
+ }
479
+
480
+ /* No `column-gap` on the grid: a gap leaves a hole no cell's own border
481
+ crosses, breaking the row divider into two segments. The same visual
482
+ space between key and value comes from padding instead, which is part of
483
+ each cell's own box — the border-bottom stays one unbroken line. */
484
+ .w6w-resolved-row > * {
485
+ padding: var(--w6w-sp-1-5) 0;
486
+ border-bottom: 1px solid var(--w6w-border);
487
+ }
488
+
489
+ .w6w-resolved-row:last-child > * {
490
+ border-bottom: none;
491
+ }
492
+
493
+ .w6w-resolved-label {
494
+ padding-right: var(--w6w-sp-3);
495
+ color: var(--w6w-muted);
496
+ }
497
+
498
+ /* One resolved row's value. `anywhere` (not `break-word`) because these values
499
+ are markup and URLs as often as prose: a single unbroken 40kB attribute
500
+ string would otherwise force the whole modal to scroll sideways. Full-
501
+ strength text color (not `--w6w-muted`) so it reads as the actual content,
502
+ distinct from the label beside it. */
503
+ .w6w-resolved-value {
504
+ color: var(--w6w-text);
505
+ overflow-wrap: anywhere;
506
+ min-width: 0;
507
+ }
508
+
509
+ /* The expanded form of a clipped value (`resolved-clip.ts`). A block with its
510
+ own scroll, so "Show all" can never restore the unbounded inline paste that
511
+ buried the other params and the run's result. */
512
+ .w6w-resolved-text-full {
513
+ display: block;
514
+ margin: var(--w6w-sp-1) 0;
515
+ max-height: 14em;
516
+ overflow: auto;
517
+ padding: var(--w6w-sp-2);
518
+ background: var(--w6w-panel-2);
519
+ border: 1px solid var(--w6w-border);
520
+ border-radius: var(--w6w-radius);
521
+ font-family: var(--w6w-font-mono);
522
+ font-size: 0.85em; /* lint-tokens-allow: matches the sibling code-typography rules above in this same file */
523
+ line-height: var(--w6w-lh-normal);
524
+ white-space: pre-wrap;
525
+ }
526
+
527
+ /* Link-weight toggle — this sits inside a value, so it must not read as a
528
+ second action competing with the modal's own buttons. */
529
+ .w6w-resolved-more {
530
+ background: none;
531
+ border: none;
532
+ padding: 0;
533
+ margin-left: var(--w6w-sp-2);
534
+ color: var(--w6w-accent);
535
+ font-size: 0.85em; /* lint-tokens-allow: matches the sibling code-typography rules above in this same file */
536
+ cursor: pointer;
537
+ white-space: nowrap;
538
+ }
539
+
540
+ .w6w-resolved-more:hover {
541
+ text-decoration: underline;
542
+ }
@@ -0,0 +1,38 @@
1
+ /* Buttons */
2
+ .w6w-btn {
3
+ background: var(--w6w-accent);
4
+ color: white;
5
+ border: none;
6
+ border-radius: 8px;
7
+ padding: var(--w6w-sp-2) 14px;
8
+ font-size: var(--w6w-fs-sm);
9
+ cursor: pointer;
10
+ }
11
+ .w6w-btn:hover {
12
+ filter: brightness(1.08);
13
+ }
14
+ .w6w-btn:disabled {
15
+ opacity: 0.5;
16
+ cursor: default;
17
+ }
18
+ .w6w-btn-ghost {
19
+ background: transparent;
20
+ border: 1px solid var(--w6w-border);
21
+ color: var(--w6w-text);
22
+ }
23
+ .w6w-btn-ghost:hover {
24
+ background: var(--w6w-panel-2);
25
+ }
26
+ .w6w-btn-ghost.active {
27
+ border-color: var(--w6w-accent);
28
+ background: var(--w6w-panel-2);
29
+ }
30
+ /* Destructive confirm button (ConfirmModal's default). Styled from the same
31
+ --w6w-danger token the error notices and health pills already use. */
32
+ .w6w-btn-danger {
33
+ background: var(--w6w-danger);
34
+ color: white;
35
+ }
36
+
37
+ /* List row — a clean, reusable list item (leading icon · title/subtitle · trailing).
38
+ Mirrors the ghost saved-test button rows it replaces. */