flexdesk 0.17.18 → 0.18.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 (118) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +1 -0
  3. package/css/flexdesk.css +7544 -5916
  4. package/css/reset.css +1 -1
  5. package/css/tokens.css +23 -1
  6. package/dist/charts.js +10 -6
  7. package/dist/charts.js.map +1 -1
  8. package/dist/chunk-5W4EEKSK.js +778 -0
  9. package/dist/chunk-5W4EEKSK.js.map +7 -0
  10. package/dist/chunk-DNJ3N4T4.js +82 -0
  11. package/dist/chunk-DNJ3N4T4.js.map +7 -0
  12. package/dist/{chunk-REJ5HLIM.js → chunk-HYJYYEPK.js} +2 -2
  13. package/dist/{chunk-7U4IB65H.js → chunk-LLBJ2SJH.js} +5 -3
  14. package/dist/{chunk-7U4IB65H.js.map → chunk-LLBJ2SJH.js.map} +1 -1
  15. package/dist/{chunk-7OL6F2AM.js → chunk-P74F6I6P.js} +6 -4
  16. package/dist/{chunk-7OL6F2AM.js.map → chunk-P74F6I6P.js.map} +1 -1
  17. package/dist/chunk-PAI2ZUWV.js +260 -0
  18. package/dist/chunk-PAI2ZUWV.js.map +7 -0
  19. package/dist/chunk-Q7BALEPX.js +527 -0
  20. package/dist/chunk-Q7BALEPX.js.map +7 -0
  21. package/dist/chunk-QXAOTGCC.js +40 -0
  22. package/dist/chunk-QXAOTGCC.js.map +7 -0
  23. package/dist/{chunk-XJNADRGR.js → chunk-RKVYIXDC.js} +664 -91
  24. package/dist/chunk-RKVYIXDC.js.map +7 -0
  25. package/dist/{chunk-2Z2ENHR3.js → chunk-U5GKJUOC.js} +3 -3
  26. package/dist/{chunk-2Z2ENHR3.js.map → chunk-U5GKJUOC.js.map} +1 -1
  27. package/dist/chunk-UKP22RUG.js +219 -0
  28. package/dist/chunk-UKP22RUG.js.map +7 -0
  29. package/dist/chunk-VLOAI3HS.js +43 -0
  30. package/dist/chunk-VLOAI3HS.js.map +7 -0
  31. package/dist/{chunk-WUFLGETN.js → chunk-WVFGV5FT.js} +2 -39
  32. package/dist/{chunk-WUFLGETN.js.map → chunk-WVFGV5FT.js.map} +3 -3
  33. package/dist/{chunk-PGZ4EHSE.js → chunk-ZL3VKINP.js} +41 -390
  34. package/dist/chunk-ZL3VKINP.js.map +7 -0
  35. package/dist/editor.js +3 -2
  36. package/dist/editor.js.map +1 -1
  37. package/dist/flexdesk.css +7544 -5916
  38. package/dist/flow.js +29 -97
  39. package/dist/flow.js.map +3 -3
  40. package/dist/git.js +1356 -0
  41. package/dist/git.js.map +7 -0
  42. package/dist/reset.css +1 -1
  43. package/dist/tiles.js +20 -15
  44. package/dist/tiles.js.map +2 -2
  45. package/dist/tokens.css +23 -1
  46. package/dist/widgets.js +3967 -1188
  47. package/dist/widgets.js.map +4 -4
  48. package/dist/wm.js +120 -32
  49. package/dist/wm.js.map +2 -2
  50. package/docs/README.md +9 -4
  51. package/docs/best-practices.md +2 -2
  52. package/docs/components/chat-shell.md +198 -0
  53. package/docs/components/conversation.md +10 -0
  54. package/docs/components/filter-builder.md +246 -0
  55. package/docs/components/forms-and-dialogs.md +22 -0
  56. package/docs/components/git.md +355 -0
  57. package/docs/components/input-fields.md +167 -2
  58. package/docs/components/keymap.md +8 -0
  59. package/docs/components/menus.md +82 -2
  60. package/docs/components/panels-and-desktops.md +16 -5
  61. package/docs/components/shell.md +104 -1
  62. package/docs/components/workspace-picker.md +149 -0
  63. package/docs/concepts/building-and-packaging.md +2 -2
  64. package/docs/concepts/packages-and-entry-points.md +14 -13
  65. package/docs/concepts/state-guard.md +3 -3
  66. package/docs/quickstart.md +1 -1
  67. package/git.js +13 -0
  68. package/package.json +6 -1
  69. package/src/flow/kit/dom.js +28 -119
  70. package/src/flow/kit/step_picker.js +1 -1
  71. package/src/flow/kit/value_picker.js +1 -1
  72. package/src/flow/outline/menu.js +1 -1
  73. package/src/git/diff_viewer.js +212 -0
  74. package/src/git/diff_window.js +51 -0
  75. package/src/git/git_graph.js +401 -0
  76. package/src/git/git_ops.js +61 -0
  77. package/src/git/git_panel.js +755 -0
  78. package/src/git/index.js +9 -0
  79. package/src/git/line_diff.js +202 -0
  80. package/src/tiles/tile_base.js +5 -4
  81. package/src/tiling/desktops.js +10 -8
  82. package/src/tiling/keymap.js +6 -5
  83. package/src/tiling/kind_taxonomy.js +15 -2
  84. package/src/tiling/shell.js +124 -17
  85. package/src/tiling/tile_renderer.js +2 -3
  86. package/src/ui/components/action_dropdown.js +4 -78
  87. package/src/ui/components/anchored_layer.js +255 -0
  88. package/src/ui/components/chat_shell.js +920 -0
  89. package/src/ui/components/chat_state_machine.js +223 -0
  90. package/src/ui/components/choice_controls.js +45 -2
  91. package/src/ui/components/color_field.js +3 -2
  92. package/src/ui/components/color_list_field.js +3 -2
  93. package/src/ui/components/combobox.js +422 -0
  94. package/src/ui/components/conversation_view.js +17 -2
  95. package/src/ui/components/data_table.js +2 -2
  96. package/src/ui/components/filter_builder.js +766 -0
  97. package/src/ui/components/filter_model.js +614 -0
  98. package/src/ui/components/managed_window.js +24 -47
  99. package/src/ui/components/modal.js +79 -109
  100. package/src/ui/components/option_list.js +160 -0
  101. package/src/ui/components/popover.js +94 -0
  102. package/src/ui/components/recent_items.js +169 -0
  103. package/src/ui/components/select.js +322 -0
  104. package/src/ui/components/tag_input.js +77 -8
  105. package/src/ui/components/workspace_picker.js +1155 -0
  106. package/src/ui/utils/fire_event.js +17 -0
  107. package/src/ui/utils/focusable.js +25 -0
  108. package/src/ui/utils/modal_host.js +27 -0
  109. package/widgets.js +12 -2
  110. package/css/base.css +0 -10547
  111. package/css/overrides.css +0 -2596
  112. package/dist/chunk-7JI7HHSY.js +0 -107
  113. package/dist/chunk-7JI7HHSY.js.map +0 -7
  114. package/dist/chunk-MIW7D6WC.js +0 -526
  115. package/dist/chunk-MIW7D6WC.js.map +0 -7
  116. package/dist/chunk-PGZ4EHSE.js.map +0 -7
  117. package/dist/chunk-XJNADRGR.js.map +0 -7
  118. /package/dist/{chunk-REJ5HLIM.js.map → chunk-HYJYYEPK.js.map} +0 -0
package/css/overrides.css DELETED
@@ -1,2596 +0,0 @@
1
- /* twm — framework rules that must WIN. Loads LAST: these deliberately beat a vendor or embedder rule they tie with on specificity.
2
- *
3
- * GENERATED by scripts/css_extract.py — do not edit by hand.
4
- */
5
-
6
- /* ── from ecoagent_extra_modes.css ───────────────────────── */
7
- /* SortableList — EcoAgent tiling shell skin. Narrower row padding,
8
- * darker recessed bg, square corners. Scoped to .twm-page-shell so the
9
- * notebook test editor and other contexts keep their original look. */
10
- .twm-page-shell .twm-sortable-list { gap: 4px; }
11
- .twm-page-shell .twm-sortable-list__items { gap: 0; }
12
- .twm-page-shell .twm-sortable-list__row {
13
- background: var(--ea-bg-content, #212121);
14
- border: 1px solid var(--ea-border, #333);
15
- border-radius: 0;
16
- border-bottom-width: 0;
17
- padding: 3px 6px;
18
- gap: 6px;
19
- }
20
- .twm-page-shell .twm-sortable-list__row:last-child {
21
- border-bottom-width: 1px;
22
- }
23
- .twm-page-shell .twm-sortable-list__row:hover { border-color: var(--ea-border, #555); }
24
- .twm-page-shell .twm-sortable-list__handle { padding: 0; opacity: 0.5; }
25
- .twm-page-shell .twm-sortable-list__remove { padding: 2px; border-radius: 0; }
26
- .twm-page-shell .twm-sortable-list__add {
27
- margin-top: 4px;
28
- border-radius: 0;
29
- border-color: var(--ea-border, #333);
30
- background: var(--ea-bg-content, #212121);
31
- padding: 4px 8px;
32
- font-size: 11px;
33
- }
34
- .twm-page-shell .twm-sortable-list__empty {
35
- border-radius: 0;
36
- border-color: var(--ea-border, #333);
37
- background: transparent;
38
- padding: 8px;
39
- font-size: 11px;
40
- }
41
- .twm-page-shell .twm-sortable-list__input {
42
- border-radius: 0;
43
- background: var(--ea-bg-well, #0d0d0d);
44
- border-color: var(--ea-border, #333);
45
- padding: 3px 6px;
46
- }
47
- .twm-entity-header__spacer { flex: 1; }
48
- /* Inline field in the detail header (sector tab's country picker,
49
- * agent tab's role badges, etc.). Density-pass v3: aligned to the
50
- * shared `--ea-row-h` height so every control in every tab header
51
- * is exactly the same vertical size — no per-page drift between
52
- * sector / agent / market / scenario headers. */
53
- .twm-entity-header__field {
54
- display: inline-flex;
55
- align-items: center;
56
- gap: var(--ea-pad-tight);
57
- font-size: 10.5px;
58
- color: var(--ea-text-muted);
59
- text-transform: uppercase;
60
- letter-spacing: 0.04em;
61
- margin-left: var(--ea-pad-snug);
62
- }
63
- .twm-entity-header__field span { line-height: 1; }
64
- .twm-entity-header__select {
65
- background: var(--ea-bg-well);
66
- color: var(--ea-text);
67
- border: 1px solid var(--ea-border);
68
- border-radius: 2px;
69
- padding: 0 var(--ea-pad-snug);
70
- height: var(--ea-row-h);
71
- font-size: var(--ea-font-body);
72
- text-transform: none;
73
- letter-spacing: 0;
74
- min-width: 110px;
75
- line-height: 1;
76
- }
77
- .twm-entity-header__select:focus {
78
- outline: none;
79
- border-color: var(--ea-accent);
80
- }
81
- .twm-entity-header__currency {
82
- color: var(--ea-text-muted);
83
- font-size: var(--ea-font-body);
84
- margin-left: var(--ea-pad-snug);
85
- font-variant-numeric: tabular-nums;
86
- }
87
- /* ─── Buttons ──────────────────────────────────────────────────────
88
- * One canonical button across the app:
89
- * - Single height (--ea-row-h, 18 px) so toolbars line up with
90
- * table rows and chrome strips.
91
- * - 2 px radius — squared off, "tool" feel rather than "website".
92
- * - Variants change colour, not size: --primary, --danger, --icon.
93
- * - --small only nudges font/padding; height stays the row token.
94
- */
95
- .twm-btn {
96
- display: inline-flex;
97
- align-items: center;
98
- justify-content: center;
99
- gap: var(--ea-pad-tight);
100
- background: #2e2e2e;
101
- color: var(--ea-text);
102
- border: 1px solid var(--ea-border);
103
- padding: 0 var(--ea-pad-snug);
104
- border-radius: 2px;
105
- cursor: pointer;
106
- font: inherit;
107
- font-size: var(--ea-font-body);
108
- line-height: 1;
109
- min-height: var(--ea-row-h);
110
- white-space: nowrap;
111
- transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
112
- }
113
- .twm-btn:hover:not(:disabled) {
114
- background: #3a3a3a;
115
- border-color: #444;
116
- }
117
- .twm-btn:active:not(:disabled) { background: #262626; }
118
- .twm-btn:focus-visible {
119
- outline: none;
120
- border-color: var(--ea-accent);
121
- box-shadow: 0 0 0 1px var(--ea-accent);
122
- }
123
- .twm-btn:disabled {
124
- opacity: 0.4;
125
- cursor: not-allowed;
126
- }
127
- .twm-btn--primary {
128
- background: var(--ea-accent);
129
- border-color: var(--ea-accent);
130
- color: var(--text-bright);
131
- }
132
- .twm-btn--primary:hover:not(:disabled) {
133
- background: var(--accent-bright-hover);
134
- border-color: var(--accent-bright-hover);
135
- }
136
- /* Context menu separator (matches Ecosim's .twm-context-menu look). */
137
- .twm-context-menu__separator {
138
- height: 1px;
139
- background: var(--surface-3);
140
- margin: var(--space-xs) 0;
141
- }
142
- /* Project sidebar — the sidebar only carries a single "Files"
143
- * section, and the redundant collapsible header above the tree was
144
- * pure chrome with no information. Hide it so the tree starts at
145
- * the top of the panel. The section box still wraps the tree (so
146
- * `.sidebar-body--accordion`'s flex layout keeps working) — only
147
- * its header is hidden.
148
- *
149
- * NOTE: `buildBrowserPanel` renders the section box with class
150
- * `tree-category dynamic-sheet-section` (not `collapsible-box`); an
151
- * earlier attempt used the wrong parent class and didn't take. */
152
- [data-panel-content="project"] .twm-tree-category > .twm-collapsible-header {
153
- display: none !important;
154
- }
155
- [data-panel-content="project"] .twm-tree-category > .twm-collapsible-content {
156
- /* Force-visible since the toggle header is gone — would
157
- * otherwise stay collapsed if persisted state said so. */
158
- display: block !important;
159
- visibility: visible !important;
160
- }
161
- [data-panel-content="project"] .twm-tree-category {
162
- border: none;
163
- margin: 0;
164
- }
165
- /* Danger / icon-only variants */
166
- .twm-btn--danger {
167
- background: #4a2424;
168
- border-color: #6a2e2e;
169
- color: #e8b0b0;
170
- }
171
- .twm-btn--danger:hover:not(:disabled) {
172
- background: #6a2e2e;
173
- color: var(--text-bright);
174
- }
175
- /* ════════════════════════════════════════════════════════════════════
176
- * ManagedWindow modal — TUI / console input-mask aesthetic.
177
- *
178
- * Reads as three deliberate horizontal zones:
179
- * ┌─ title strip (chrome bg, marker + uppercase title, 26 px) ────┐
180
- * │ body (content bg, fielded form: label : input grid) │
181
- * └─ action strip (chrome bg, buttons right-aligned, 32 px) ──────┘
182
- *
183
- * Outer frame: 1 px hairline + 1 px inner accent line via box-shadow
184
- * inset, giving it a Borland-drawn-box feel without literal 2px borders.
185
- * ════════════════════════════════════════════════════════════════════ */
186
-
187
- .twm-managed-window.twm-managed-window--modal {
188
- background: var(--ea-bg-content);
189
- border: 1px solid #4a4a4a;
190
- border-radius: 0;
191
- box-shadow:
192
- 0 1px 0 rgba(255, 255, 255, 0.04) inset,
193
- 0 0 0 1px rgba(0, 0, 0, 0.6),
194
- 0 18px 48px rgba(0, 0, 0, 0.55),
195
- 0 4px 16px rgba(0, 0, 0, 0.45);
196
- overflow: hidden;
197
- color: var(--ea-text);
198
- }
199
- /* Title strip — like the title bar of a Borland Turbo Vision dialog:
200
- * solid dark band, title in bold uppercase with a small accent marker
201
- * (▸) so it reads as "active". The strip is taller than the body's
202
- * usual 18 px row to feel like a band, not a line. */
203
- .twm-managed-window--modal .twm-managed-window__topbar {
204
- background:
205
- linear-gradient(180deg,
206
- rgba(255, 255, 255, 0.025) 0%,
207
- rgba(0, 0, 0, 0) 70%),
208
- var(--ea-bg-chrome);
209
- border-bottom: 1px solid #4a4a4a;
210
- border-radius: 0;
211
- min-height: 26px;
212
- padding: 0 var(--ea-pad-loose) 0 calc(var(--ea-pad-loose) - 4px);
213
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
214
- }
215
- .twm-managed-window--modal .twm-managed-window__icon {
216
- color: var(--ea-accent);
217
- font-size: 14px;
218
- margin-right: 6px;
219
- }
220
- .twm-managed-window--modal .twm-managed-window__title {
221
- color: var(--ea-text);
222
- font-size: var(--ea-font-body);
223
- font-weight: 700;
224
- letter-spacing: 0.08em;
225
- text-transform: uppercase;
226
- }
227
- .twm-managed-window--modal .twm-managed-window__title::before {
228
- content: '▸ ';
229
- color: var(--ea-accent);
230
- margin-right: 2px;
231
- }
232
- .twm-managed-window--modal .twm-managed-window__btn--close {
233
- width: 22px;
234
- height: 22px;
235
- color: var(--ea-text-muted);
236
- background: transparent;
237
- border-radius: 0;
238
- }
239
- .twm-managed-window--modal .twm-managed-window__btn--close:hover {
240
- background: #b03a3a;
241
- color: #fff;
242
- }
243
- /* Content area — drop the host padding so the body host controls its
244
- * own zones (form rows + flush-bottom action strip). The content never
245
- * scrolls itself: the zone above the strip does, so the strip stays whole
246
- * and in view when the fields outgrow the window. */
247
- .twm-managed-window--modal .twm-managed-window__content {
248
- background: var(--ea-bg-content);
249
- padding: 0;
250
- overflow: hidden;
251
- }
252
- .twm-managed-window--modal .twm-modal__body-host {
253
- display: flex;
254
- flex-direction: column;
255
- height: 100%;
256
- }
257
- /* Form sits inside the body host as a flex column: the fields take the
258
- * room left over and scroll, the error banner and the actions row keep
259
- * their height below them. */
260
- .twm-managed-window--modal .twm-modal__form {
261
- display: flex;
262
- flex-direction: column;
263
- flex: 1 1 auto;
264
- min-height: 0;
265
- padding: 0;
266
- overflow: hidden;
267
- }
268
- .twm-managed-window--modal .twm-modal__fields {
269
- flex: 1 1 auto;
270
- min-height: 0;
271
- overflow-y: auto;
272
- padding-bottom: 14px;
273
- }
274
- .twm-managed-window--modal .twm-modal__form > .twm-modal__error,
275
- .twm-managed-window--modal .twm-modal__form > .twm-modal__actions {
276
- flex: 0 0 auto;
277
- }
278
- .twm-managed-window--modal .twm-modal__form > .twm-modal__error {
279
- margin-bottom: 8px;
280
- }
281
- /* Field rows get inset margins so the action strip can run edge-to-edge
282
- * as a true bottom band. */
283
- .twm-managed-window--modal .twm-modal__fields > .twm-modal__row,
284
- .twm-managed-window--modal .twm-modal__fields > .twm-modal__section,
285
- .twm-managed-window--modal .twm-modal__form > .twm-modal__error,
286
- .twm-managed-window--modal .twm-modal__body {
287
- margin-left: 14px;
288
- margin-right: 14px;
289
- }
290
- .twm-managed-window--modal .twm-modal__fields > .twm-modal__row:first-of-type,
291
- .twm-managed-window--modal .twm-modal__fields > .twm-modal__section:first-child,
292
- .twm-managed-window--modal .twm-modal__body:first-child {
293
- margin-top: 14px;
294
- }
295
- /* Rich-content body (openModal). Fills the available height between
296
- * the title strip and the action footer; the caller's content sits
297
- * inside and inherits the scroll. */
298
- .twm-managed-window--modal .twm-modal__body--rich {
299
- margin: 0 14px 0;
300
- padding: 0;
301
- flex: 1 1 auto;
302
- min-height: 0;
303
- overflow: auto;
304
- display: flex;
305
- flex-direction: column;
306
- }
307
- .twm-managed-window--modal .twm-modal__body--rich > * {
308
- flex: 1 1 auto;
309
- min-height: 0;
310
- }
311
- /* Fielded input mask: label right-aligned with a colon suffix, input
312
- * occupying the remaining width. Mirrors how curses/dialog renders a
313
- * form — labels read down the left edge, values down the right. */
314
- .twm-managed-window--modal .twm-modal__row {
315
- display: grid;
316
- grid-template-columns: 130px 1fr;
317
- align-items: center;
318
- column-gap: 10px;
319
- row-gap: 0;
320
- margin-bottom: 10px;
321
- }
322
- .twm-managed-window--modal .twm-modal__row--multiline {
323
- align-items: start;
324
- }
325
- .twm-managed-window--modal .twm-modal__row > span:first-child {
326
- color: var(--ea-text-dim);
327
- font-size: var(--ea-font-body);
328
- text-transform: uppercase;
329
- letter-spacing: 0.05em;
330
- text-align: right;
331
- justify-self: end;
332
- line-height: var(--ea-row-h);
333
- }
334
- /* Add the trailing colon via CSS so callers don't have to. Suppress on
335
- * checkbox rows (their left cell is intentionally empty). */
336
- .twm-managed-window--modal .twm-modal__row > span:first-child:not(:empty)::after {
337
- content: ' :';
338
- color: var(--ea-text-muted);
339
- margin-left: 1px;
340
- }
341
- .twm-managed-window--modal .twm-modal__row.twm-is-invalid > span:first-child {
342
- color: #f0a8a8;
343
- }
344
- /* Input wells — deeper than the body so the field reads as "the place
345
- * you type" against the panel. Sharp 1 px border with a 1 px inner
346
- * shadow that simulates being inset. */
347
- .twm-managed-window--modal .twm-modal__row input[type="text"],
348
- .twm-managed-window--modal .twm-modal__row input[type="number"],
349
- .twm-managed-window--modal .twm-modal__row input[type="password"],
350
- .twm-managed-window--modal .twm-modal__row input[type="search"],
351
- .twm-managed-window--modal .twm-modal__row input[type="email"],
352
- .twm-managed-window--modal .twm-modal__row select,
353
- .twm-managed-window--modal .twm-modal__row textarea {
354
- background: #0a0a0a;
355
- color: var(--ea-text);
356
- border: 1px solid #3a3a3a;
357
- border-radius: 0;
358
- /* Shared input padding (same token as `.ea-tin`). Was `0 8px` with a
359
- * line-height fudge for vertical centering — real padding instead. */
360
- padding: var(--ea-input-pad);
361
- min-height: 22px;
362
- line-height: normal;
363
- font: inherit;
364
- font-size: var(--ea-font-body);
365
- width: 100%;
366
- box-sizing: border-box;
367
- box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4) inset;
368
- }
369
- .twm-managed-window--modal .twm-modal__row textarea {
370
- line-height: 1.45;
371
- padding: 6px 8px;
372
- min-height: calc(var(--ea-row-h) * 4);
373
- resize: vertical;
374
- }
375
- .twm-managed-window--modal .twm-modal__row input:focus,
376
- .twm-managed-window--modal .twm-modal__row select:focus,
377
- .twm-managed-window--modal .twm-modal__row textarea:focus {
378
- outline: none;
379
- border-color: var(--ea-accent);
380
- box-shadow:
381
- 0 1px 0 rgba(0, 0, 0, 0.4) inset,
382
- 0 0 0 1px var(--ea-accent);
383
- }
384
- .twm-managed-window--modal .twm-modal__row.twm-is-invalid input,
385
- .twm-managed-window--modal .twm-modal__row.twm-is-invalid select,
386
- .twm-managed-window--modal .twm-modal__row.twm-is-invalid textarea {
387
- border-color: #b03a3a;
388
- }
389
- /* Hint + per-field error — small dim line that sits *under* the input,
390
- * spanning only the input column so it aligns with the field above. */
391
- .twm-managed-window--modal .twm-modal__hint--field {
392
- grid-column: 2;
393
- color: var(--ea-text-muted);
394
- font-size: 10.5px;
395
- margin-top: 3px;
396
- line-height: 1.4;
397
- font-style: italic;
398
- }
399
- .twm-managed-window--modal .twm-modal__field-error {
400
- grid-column: 2;
401
- color: #f0a8a8;
402
- font-size: 10.5px;
403
- margin-top: 3px;
404
- line-height: 1.4;
405
- }
406
- /* Section divider — small uppercase heading with a long em-dash style
407
- * underline that runs to the right edge of the field column. Reads
408
- * like the section breaks in a dialog/ncurses form. */
409
- .twm-managed-window--modal .twm-modal__section {
410
- grid-column: 1 / -1;
411
- display: flex;
412
- align-items: center;
413
- gap: 8px;
414
- margin: 14px 0 8px;
415
- padding: 0;
416
- font-size: 10.5px;
417
- font-weight: 700;
418
- text-transform: uppercase;
419
- letter-spacing: 0.08em;
420
- color: var(--ea-accent);
421
- }
422
- .twm-managed-window--modal .twm-modal__section::after {
423
- content: '';
424
- flex: 1;
425
- height: 1px;
426
- background: #3a3a3a;
427
- }
428
- /* Inline checkbox row — left cell empty (kept for grid alignment), the
429
- * checkbox + its own label live in column 2. */
430
- .twm-managed-window--modal .twm-modal__check {
431
- display: flex;
432
- align-items: center;
433
- gap: 8px;
434
- color: var(--ea-text);
435
- }
436
- .twm-managed-window--modal .twm-modal__check input[type="checkbox"] {
437
- width: 14px;
438
- height: 14px;
439
- margin: 0;
440
- accent-color: var(--ea-accent);
441
- }
442
- /* Combobox = input + datalist + a small "↳ will create X" hint. The
443
- * hint sits below the input, dim accent color. */
444
- .twm-managed-window--modal .twm-combobox {
445
- display: flex;
446
- flex-direction: column;
447
- gap: 2px;
448
- }
449
- .twm-managed-window--modal .twm-combobox__hint {
450
- font-size: 10.5px;
451
- color: var(--ea-accent);
452
- font-style: italic;
453
- }
454
- /* Action strip — true footer band, flush against the modal edges,
455
- * darker chrome bg, top border + 1px highlight above. Right-aligned
456
- * buttons, primary stays rightmost. */
457
- .twm-managed-window--modal .twm-modal__actions {
458
- margin-top: auto;
459
- display: flex;
460
- justify-content: flex-end;
461
- align-items: center;
462
- gap: 6px;
463
- background: var(--ea-bg-chrome);
464
- border-top: 1px solid #4a4a4a;
465
- box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.03) inset;
466
- padding: 6px 14px;
467
- min-height: 32px;
468
- }
469
- .twm-managed-window--modal .twm-modal__actions .twm-btn {
470
- border-radius: 0;
471
- min-height: 22px;
472
- padding: 0 14px;
473
- font-size: var(--ea-font-body);
474
- letter-spacing: 0.04em;
475
- text-transform: uppercase;
476
- }
477
- .twm-managed-window--modal .twm-modal__actions .twm-btn--primary {
478
- background: var(--ea-accent);
479
- border-color: var(--ea-accent);
480
- color: #fff;
481
- font-weight: 600;
482
- }
483
- .twm-managed-window--modal .twm-modal__actions .twm-btn--primary:hover:not(:disabled) {
484
- background: var(--accent-bright-hover);
485
- border-color: var(--accent-bright-hover);
486
- }
487
- .twm-managed-window--modal .twm-modal__actions .twm-btn--danger {
488
- background: #8b2a2a;
489
- border-color: #8b2a2a;
490
- color: #fff;
491
- }
492
- .twm-managed-window--modal .twm-modal__actions .twm-btn--danger:hover:not(:disabled) {
493
- background: #a13434;
494
- border-color: #a13434;
495
- }
496
- /* openConfirm uses .twm-modal__body for the message paragraph. */
497
- .twm-managed-window--modal .twm-modal__body {
498
- padding: 0;
499
- line-height: 1.5;
500
- color: var(--ea-text);
501
- flex: 1 1 auto;
502
- min-height: 0;
503
- overflow-y: auto;
504
- }
505
- /* openConfirm's message is text (BUG-0014): `\n` breaks the line, and a long
506
- * unbroken name wraps instead of widening the dialog. */
507
- .twm-managed-window--modal .twm-modal__body--text {
508
- white-space: pre-line;
509
- overflow-wrap: anywhere;
510
- }
511
- /* Form-level error banner — left accent stripe, dim red panel. */
512
- .twm-managed-window--modal .twm-modal__error {
513
- margin-top: 8px;
514
- padding: 6px 10px;
515
- background: rgba(176, 58, 58, 0.12);
516
- color: #f0a8a8;
517
- border: 1px solid #6a2828;
518
- border-left: 3px solid #b03a3a;
519
- font-size: 10.5px;
520
- line-height: 1.4;
521
- }
522
- .twm-modal__form,
523
- .twm-modal__body {
524
- padding: var(--ea-pad-loose);
525
- overflow: auto;
526
- }
527
- .twm-modal__row {
528
- display: grid;
529
- grid-template-columns: minmax(120px, 32%) 1fr;
530
- align-items: center;
531
- gap: var(--ea-pad-loose);
532
- margin-bottom: var(--ea-pad-snug);
533
- font-size: var(--ea-font-body);
534
- color: var(--ea-text);
535
- }
536
- .twm-modal__row--multiline {
537
- align-items: start;
538
- }
539
- .twm-modal__row > span {
540
- color: var(--ea-text-dim);
541
- font-size: var(--ea-font-body);
542
- padding-top: 1px;
543
- }
544
- .twm-modal__row input[type="text"],
545
- .twm-modal__row input[type="number"],
546
- .twm-modal__row input[type="password"],
547
- .twm-modal__row input[type="search"],
548
- .twm-modal__row input[type="email"],
549
- .twm-modal__row select,
550
- .twm-modal__row textarea {
551
- background: var(--ea-bg-well);
552
- color: var(--ea-text);
553
- border: 1px solid var(--ea-border);
554
- padding: 0 var(--ea-pad-snug);
555
- border-radius: 0;
556
- font: inherit;
557
- font-size: var(--ea-font-body);
558
- width: 100%;
559
- box-sizing: border-box;
560
- }
561
- .twm-modal__row textarea {
562
- padding: var(--ea-pad-snug);
563
- min-height: calc(var(--ea-row-h) * 3);
564
- line-height: 1.45;
565
- resize: vertical;
566
- font-family: inherit;
567
- }
568
- .twm-modal__row input:focus,
569
- .twm-modal__row select:focus,
570
- .twm-modal__row textarea:focus {
571
- outline: none;
572
- border-color: var(--ea-accent);
573
- box-shadow: 0 0 0 1px var(--ea-accent);
574
- }
575
- .twm-modal__row.twm-is-invalid input,
576
- .twm-modal__row.twm-is-invalid select,
577
- .twm-modal__row.twm-is-invalid textarea {
578
- border-color: #b03a3a;
579
- }
580
- /* Per-field help text — small dim line that sits under the row,
581
- * spanning only the input column so it aligns with the field. */
582
- .twm-modal__hint--field {
583
- grid-column: 2;
584
- color: var(--ea-text-dim);
585
- font-size: var(--ea-font-small, 11px);
586
- margin-top: 2px;
587
- margin-bottom: var(--ea-pad-snug);
588
- line-height: 1.4;
589
- }
590
- .twm-modal__field-error {
591
- grid-column: 2;
592
- color: #f0a8a8;
593
- font-size: var(--ea-font-small, 11px);
594
- margin-top: 2px;
595
- margin-bottom: var(--ea-pad-snug);
596
- line-height: 1.4;
597
- }
598
- /* Section divider — small uppercase label with a thin top border, used
599
- * to group fields inside a longer form. */
600
- .twm-modal__section {
601
- grid-column: 1 / -1;
602
- margin: var(--ea-pad-loose) 0 var(--ea-pad-snug);
603
- padding-top: var(--ea-pad-snug);
604
- border-top: 1px solid var(--ea-border);
605
- font-size: var(--ea-font-small, 11px);
606
- text-transform: uppercase;
607
- letter-spacing: 0.06em;
608
- color: var(--ea-text-muted);
609
- }
610
- .twm-modal__section:first-child {
611
- margin-top: 0;
612
- padding-top: 0;
613
- border-top: none;
614
- }
615
- /* Inline checkbox — label sits on the right of the box so the row
616
- * reads naturally. The empty <span> in column 1 keeps grid alignment
617
- * with text-input rows. */
618
- .twm-modal__check {
619
- display: flex;
620
- align-items: center;
621
- gap: var(--ea-pad-snug);
622
- color: var(--ea-text);
623
- }
624
- .twm-modal__check input[type="checkbox"] {
625
- width: 14px;
626
- height: 14px;
627
- margin: 0;
628
- accent-color: var(--ea-accent);
629
- }
630
- /* Dependent-property pick-or-create — a type-ahead combobox. The user
631
- picks an existing option or types a new value (created on submit).
632
- With no options it's just a text field — the "nothing defined yet"
633
- case needs no special handling. */
634
- .twm-combobox {
635
- display: flex;
636
- flex-direction: column;
637
- gap: 3px;
638
- }
639
- .twm-combobox input { width: 100%; box-sizing: border-box; }
640
- .twm-combobox__hint {
641
- font-size: var(--ea-font-small, 11px);
642
- color: var(--ea-accent);
643
- }
644
- .twm-modal__actions {
645
- display: flex;
646
- justify-content: flex-end;
647
- align-items: center;
648
- gap: var(--ea-pad-snug);
649
- background: var(--ea-bg-chrome);
650
- }
651
- .twm-modal__actions .twm-btn {
652
- border-radius: 0;
653
- min-height: calc(var(--ea-row-h) + 4px);
654
- padding: 0 var(--ea-pad-loose);
655
- }
656
- .twm-modal__error {
657
- margin: 0 var(--ea-pad-loose) var(--ea-pad-snug);
658
- padding: var(--ea-pad-snug) var(--ea-pad-loose);
659
- border-radius: 0;
660
- background: #3a1f1f;
661
- color: #f0a8a8;
662
- border: 1px solid #5a2a2a;
663
- font-size: var(--ea-font-body);
664
- }
665
- .twm-modal__body {
666
- padding: var(--ea-pad-loose);
667
- }
668
- .twm-slide-out-panel__body .twm-autocomplete-field input,
669
- .twm-slide-out-panel__body .twm-autocomplete-field select,
670
- .twm-slide-out-panel__body .twm-autocomplete-field button {
671
- height: var(--ea-row-h);
672
- min-height: var(--ea-row-h);
673
- font-size: var(--ea-font-body);
674
- padding: 0 var(--ea-pad-snug);
675
- background: var(--ea-bg-well);
676
- color: var(--ea-text);
677
- border: 1px solid var(--ea-border);
678
- border-radius: 2px;
679
- line-height: 1;
680
- }
681
- /* ══════════════════════════════════════════════════════════════════════
682
- * SFC mode overview — dense two-table layout (Sectors + Flows always
683
- * visible). Header strip on top with summary stats; two stacked
684
- * DataTable panes below, each independently scrollable. Both tables
685
- * are keyboard-navigable; rows are click-to-open.
686
- *
687
- * No cards, no pills, no rounded boxes — matches the dense aesthetic
688
- * of sector / market / agent tabs (.twm-entity-header + .ea-table).
689
- * ══════════════════════════════════════════════════════════════════════ */
690
- .twm-landing {
691
- display: flex;
692
- flex-direction: column;
693
- height: 100%;
694
- min-height: 0;
695
- overflow: hidden; /* keep the actions strip anchored at the bottom */
696
- background: var(--ea-bg-chrome);
697
- color: var(--ea-text);
698
- font-size: var(--ea-font-body);
699
- }
700
- .twm-landing__split {
701
- flex: 1 1 0; /* shrink-fit so footer always shows */
702
- min-height: 0;
703
- display: flex;
704
- flex-direction: column;
705
- overflow: hidden;
706
- }
707
- .twm-landing__pane {
708
- flex: 1 1 50%;
709
- min-height: 0;
710
- display: flex;
711
- flex-direction: column;
712
- border-top: 1px solid var(--ea-border);
713
- }
714
- .twm-landing__pane:first-child { border-top: none; }
715
- /* Single-pane landings (Assets / Markets / Archetypes / Agents /
716
- * Scenarios / KPIs / …) — the lone pane should fill 100% of the
717
- * available vertical space inside the split. Without this the
718
- * `flex: 1 1 50%` default leaves the pane sized to its content. */
719
- .twm-landing__split--single > .twm-landing__pane {
720
- flex: 1 1 auto;
721
- }
722
- .twm-landing__split--single > .twm-landing__pane
723
- > .twm-landing__table {
724
- flex: 1 1 0;
725
- min-height: 0;
726
- }
727
- .twm-landing__pane-head {
728
- display: flex;
729
- align-items: center;
730
- gap: var(--ea-pad-snug);
731
- padding: 0 var(--ea-pad-snug);
732
- height: 22px;
733
- min-height: 22px;
734
- background: var(--ea-bg-chrome);
735
- border-bottom: 1px solid var(--ea-border-soft);
736
- flex-shrink: 0;
737
- }
738
- .twm-landing__pane-title {
739
- font-size: 10.5px;
740
- text-transform: uppercase;
741
- letter-spacing: 0.04em;
742
- font-weight: 600;
743
- color: var(--ea-text);
744
- }
745
- .twm-landing__pane-hint {
746
- margin-left: auto;
747
- color: var(--ea-text-dim);
748
- font-size: 10px;
749
- }
750
- .twm-landing__pane--focused .twm-landing__pane-head {
751
- background: color-mix(in srgb, var(--accent-hover) 10%, transparent);
752
- }
753
- .twm-landing__pane--focused .twm-landing__pane-title {
754
- color: var(--ea-accent);
755
- }
756
- .twm-landing__table {
757
- flex: 1;
758
- min-height: 0;
759
- overflow: auto;
760
- background: var(--ea-bg-content);
761
- }
762
- /* Kind-filter dropdown in the Archetypes landing header. Same dark
763
- * styling as the rest of the inputs — the bare browser default reads
764
- * as light grey. */
765
- .twm-entity-header__field select {
766
- background-color: var(--ea-input-bg, #1e1e1e);
767
- color: var(--ea-text, #ddd);
768
- border: 1px solid var(--ea-border, #444);
769
- padding: 0.2em 1.6em 0.2em 0.5em;
770
- font: inherit;
771
- font-size: 0.85em;
772
- -webkit-appearance: none;
773
- -moz-appearance: none;
774
- appearance: none;
775
- background-image: linear-gradient(45deg, transparent 50%, var(--ea-text-muted, #888) 50%),
776
- linear-gradient(135deg, var(--ea-text-muted, #888) 50%, transparent 50%);
777
- background-position: calc(100% - 12px) 50%, calc(100% - 8px) 50%;
778
- background-size: 4px 4px, 4px 4px;
779
- background-repeat: no-repeat;
780
- cursor: pointer;
781
- }
782
- .twm-entity-header__field select:focus {
783
- outline: none;
784
- border-color: var(--ea-accent);
785
- }
786
- .twm-entity-header__field select option { background: var(--ea-input-bg, #1e1e1e); color: var(--ea-text, #ddd); }
787
- .twm-landing__table table {
788
- width: 100%;
789
- }
790
- .twm-landing__table thead th {
791
- position: sticky;
792
- top: 0;
793
- background: var(--ea-bg-chrome);
794
- z-index: 1;
795
- }
796
- .twm-landing__table tbody tr:hover {
797
- background: rgba(255, 255, 255, 0.04);
798
- }
799
- /* Keyboard cursor highlight — shared between every overview's DataTable
800
- so the same arrow-key affordance reads identically across modes. */
801
- .twm-row-cursor {
802
- box-shadow: inset 2px 0 0 var(--ea-accent);
803
- background: color-mix(in srgb, var(--accent-hover) 12%, transparent) !important;
804
- }
805
- /* Quick-action strip — horizontal bar below the tables. Same chrome
806
- tone as `.twm-entity-header` so the page reads as a single stack:
807
- header / two tables / actions. Buttons are flat (no border-radius
808
- beyond 2px), icon + short label, keyboard-shortcut hint on the right.
809
- Reused across every mode overview that wants a quick-action row. */
810
- .twm-landing__actions {
811
- display: flex;
812
- align-items: center;
813
- gap: var(--ea-pad-snug);
814
- height: 30px;
815
- min-height: 30px;
816
- padding: 0 var(--ea-pad-snug);
817
- background: var(--ea-bg-well);
818
- border-top: 1px solid var(--ea-border);
819
- flex: 0 0 30px;
820
- position: relative;
821
- z-index: 2;
822
- }
823
- .twm-landing__actions-spacer { flex: 1; }
824
- .twm-landing__actions-hint {
825
- color: var(--ea-text-dim);
826
- font-size: 10px;
827
- }
828
- .twm-action-btn {
829
- display: inline-flex;
830
- align-items: center;
831
- gap: 4px;
832
- height: 20px;
833
- padding: 0 var(--ea-pad-snug);
834
- background: transparent;
835
- border: 1px solid var(--ea-border-soft);
836
- border-radius: 2px;
837
- color: var(--ea-text);
838
- font-size: var(--ea-font-body);
839
- cursor: pointer;
840
- }
841
- .twm-action-btn:hover {
842
- border-color: var(--ea-accent);
843
- background: color-mix(in srgb, var(--accent-hover) 8%, transparent);
844
- }
845
- .twm-action-btn:disabled,
846
- .twm-action-btn[aria-disabled="true"] {
847
- color: var(--ea-text-dim);
848
- cursor: not-allowed;
849
- }
850
- /* Inline keyboard-shortcut chip rendered inside an .twm-action-btn —
851
- * the visible "[N]" that tells the user which letter fires the action.
852
- * mountLandingActions appends it after the label and `landing_table.js`
853
- * keeps the keydown handler in sync, so the chip and the hotkey can
854
- * never drift apart. */
855
- .twm-action-btn__kbd {
856
- margin-left: 4px;
857
- padding: 0 4px;
858
- font-family: var(--ea-mono, monospace);
859
- font-size: 10px;
860
- line-height: 14px;
861
- color: var(--ea-text-dim);
862
- border: 1px solid var(--ea-border-soft);
863
- border-radius: 2px;
864
- background: var(--ea-bg-strip, transparent);
865
- }
866
- .twm-action-btn:hover .twm-action-btn__kbd {
867
- color: var(--ea-text);
868
- border-color: var(--ea-accent);
869
- }
870
- /* ── Starter-gallery picker (G1.3) ─────────────────────────────────
871
- * Card grid used by every "New X" modal to choose a starter shape.
872
- * Replaces the legacy template <select> dropdowns. Cards stack
873
- * naturally into rows of 2–3 depending on modal width; selecting a
874
- * card simply toggles `.is-selected`. */
875
- .twm-gallery-picker {
876
- display: grid;
877
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
878
- gap: 8px;
879
- align-content: start;
880
- max-height: 320px;
881
- overflow-y: auto;
882
- padding: 4px;
883
- border: 1px solid var(--ea-border, #2a2a2a);
884
- background: var(--ea-input-bg, #1e1e1e);
885
- }
886
- .twm-gallery-picker__empty {
887
- grid-column: 1 / -1;
888
- padding: 16px;
889
- color: var(--ea-text-dim, #777);
890
- font-style: italic;
891
- text-align: center;
892
- }
893
- .twm-gallery-card {
894
- display: flex;
895
- flex-direction: column;
896
- gap: 4px;
897
- align-items: flex-start;
898
- text-align: left;
899
- padding: 10px 12px;
900
- background: var(--ea-bg-content, #181818);
901
- border: 1px solid var(--ea-border, #2a2a2a);
902
- color: var(--ea-text, #ddd);
903
- cursor: pointer;
904
- font-family: inherit;
905
- font-size: 13px;
906
- transition: background-color 0.08s ease,
907
- border-color 0.08s ease;
908
- }
909
- .twm-gallery-card:hover {
910
- background: rgba(255, 255, 255, 0.04);
911
- border-color: var(--ea-border-soft, #3a3a3a);
912
- }
913
- .twm-gallery-card__label {
914
- font-weight: 600;
915
- line-height: 1.2;
916
- }
917
- .twm-gallery-card__desc {
918
- margin: 0;
919
- color: var(--ea-text-muted, #999);
920
- font-size: 11.5px;
921
- line-height: 1.35;
922
- }
923
- /* Resource-kind picker — verdict-driven dropdown row tinting + the
924
- * Requirements panel below the picker. Same colour family as the
925
- * signature panel (match=green, override/partial=amber, missing=red)
926
- * so the two views read consistently. */
927
- .twm-autocomplete-field__item[data-type="ok"] { /* default */ }
928
- .twm-autocomplete-field__item[data-type="partial"] {
929
- background: rgba(204, 120, 65, 0.08);
930
- }
931
- .twm-autocomplete-field__item[data-type="missing"] {
932
- background: rgba(220, 80, 80, 0.10);
933
- color: var(--ea-text-dim, rgba(255, 255, 255, 0.55));
934
- }
935
- .twm-autocomplete-field__item[data-type="ok"] .twm-autocomplete-field__badge { color: #69f0ae; }
936
- .twm-autocomplete-field__item[data-type="partial"] .twm-autocomplete-field__badge { color: #ffd180; }
937
- .twm-autocomplete-field__item[data-type="missing"] .twm-autocomplete-field__badge { color: #f48fb1; }
938
-
939
- /* ── from tiling/tile.css ───────────────────────── */
940
- /* tile.css — WM containers, leaves, chrome, splitters. */
941
-
942
- .twm-shell__main {
943
- overflow: hidden;
944
- position: relative;
945
- }
946
- .twm-root {
947
- display: flex;
948
- height: 100%;
949
- width: 100%;
950
- background: var(--surface-app);
951
- }
952
- .twm-empty {
953
- display: flex;
954
- height: 100%;
955
- width: 100%;
956
- align-items: center;
957
- justify-content: center;
958
- color: var(--text-muted);
959
- font-size: var(--font-size-xs);
960
- }
961
- /* Split + slot — flex containers along one axis. */
962
- .twm-split {
963
- display: flex;
964
- flex: 1 1 auto;
965
- overflow: hidden;
966
- min-width: 0;
967
- min-height: 0;
968
- }
969
- .twm-split--h { flex-direction: row; }
970
- .twm-split--v { flex-direction: column; }
971
- .twm-slot {
972
- display: flex;
973
- overflow: hidden;
974
- min-width: 0;
975
- min-height: 0;
976
- }
977
- .twm-slot > .twm-leaf,
978
- .twm-slot > .twm-split { flex: 1 1 auto; min-width: 0; min-height: 0; }
979
- /* Splitter handle between siblings. */
980
- .twm-splitter {
981
- flex: 0 0 4px;
982
- background: transparent;
983
- position: relative;
984
- z-index: 1;
985
- }
986
- .twm-splitter:hover,
987
- body.twm-dragging .twm-splitter { background: var(--accent-bright); opacity: 0.5; }
988
- .twm-splitter--h { cursor: ew-resize; }
989
- .twm-splitter--v { cursor: ns-resize; height: 4px; flex: 0 0 4px; }
990
- body.twm-dragging * { user-select: none; }
991
- /* Leaf — chrome + body. */
992
- .twm-leaf {
993
- display: flex;
994
- flex-direction: column;
995
- flex: 1 1 auto;
996
- background: var(--surface-0);
997
- border: 1px solid var(--border-subtle);
998
- border-radius: 0;
999
- margin: 0;
1000
- overflow: hidden;
1001
- min-width: 0;
1002
- min-height: 0;
1003
- outline: none;
1004
- }
1005
- .twm-leaf--focused {
1006
- border-color: var(--accent-bright);
1007
- box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent-bright) 25%, transparent) inset;
1008
- }
1009
- .twm-leaf__chrome {
1010
- display: flex;
1011
- align-items: center;
1012
- justify-content: space-between;
1013
- gap: var(--space-md);
1014
- padding: 0 var(--space-sm);
1015
- height: 22px;
1016
- /* Pin the chrome like its sibling .twm-leaf__tabbar. Without
1017
- * flex-shrink:0 the chrome inherits the default `0 1 auto` and is the
1018
- * only fixed-height bar the flex column may shrink: a tab whose content
1019
- * has a tall max-content height (e.g. Watch — rows min-height:60px +
1020
- * Monaco editor + hits + help aside) inflates .twm-leaf__body's
1021
- * flex-basis:auto, and the negative free space gets distributed onto
1022
- * the chrome too, visibly changing its height. The body (overflow:auto)
1023
- * is what should absorb the overflow, never the chrome. */
1024
- flex: 0 0 auto;
1025
- background: var(--surface-1);
1026
- border-bottom: 1px solid var(--border-subtle);
1027
- user-select: none;
1028
- font-size: var(--font-size-xs);
1029
- }
1030
- .twm-leaf--focused .twm-leaf__chrome {
1031
- background: var(--surface-2);
1032
- color: var(--text-primary);
1033
- }
1034
- .twm-leaf__title {
1035
- color: var(--text-secondary);
1036
- white-space: nowrap;
1037
- text-overflow: ellipsis;
1038
- overflow: hidden;
1039
- text-transform: lowercase;
1040
- letter-spacing: 0.02em;
1041
- }
1042
- .twm-leaf__actions { display: inline-flex; gap: 2px; }
1043
- .twm-leaf__btn {
1044
- display: inline-flex;
1045
- align-items: center;
1046
- justify-content: center;
1047
- width: 18px;
1048
- height: 18px;
1049
- padding: 0;
1050
- background: transparent;
1051
- border: none;
1052
- color: var(--text-muted);
1053
- cursor: pointer;
1054
- border-radius: 0;
1055
- }
1056
- .twm-leaf__btn:hover { background: var(--state-hover); color: var(--text-primary); }
1057
- .twm-leaf__body {
1058
- flex: 1 1 auto;
1059
- overflow: auto;
1060
- min-height: 0;
1061
- background: var(--surface-0);
1062
- }
1063
- /* Bottom tab strip — appears only when the leaf carries >1 tab.
1064
- * Trapezoidal/spreadsheet-style tabs (slanted top corners) sit on a
1065
- * thin bar at the bottom of the tile, sharing the visual language of
1066
- * the bottom panel's chrome (surface-1, square corners, 11 px label).
1067
- *
1068
- * The hamburger button on the left opens the per-tile "browse this
1069
- * page's content" menu (`wm.ctx.onTileTabMenu`). */
1070
- .twm-leaf__tabbar {
1071
- flex: 0 0 auto;
1072
- display: flex;
1073
- align-items: stretch;
1074
- height: 22px;
1075
- background: var(--surface-1);
1076
- border-top: 1px solid var(--border-default);
1077
- padding: 0 4px;
1078
- overflow: hidden;
1079
- user-select: none;
1080
- font-size: var(--font-size-xs);
1081
- }
1082
- .twm-leaf__tabbar--hidden { display: none; }
1083
-
1084
- /* ══════════════════════════════════════════════════════════════════════
1085
- * C33. A TAB DRAGGED INTO A TILE
1086
- *
1087
- * Here and not in `css/base.css`, because every `.twm-leaf*` declaration
1088
- * originates in this file (`.twm-leaf` and `.twm-leaf__tabbar` above) and
1089
- * base.css carries none of them. Hand-mirrored into `css/flexdesk.css`,
1090
- * which is the only sheet `build.mjs` copies into `dist/` and whose
1091
- * generator (`scripts/css_extract.py`) is not in this repository.
1092
- *
1093
- * THE ZONE RECTANGLE IS NOT STYLED HERE. It reuses `.twm-snap-preview
1094
- * .twm-snap-preview--viewport` — the window drop's own preview element,
1095
- * already defined in base.css — so a tab drop and a window drop cannot come
1096
- * to disagree about what a drop looks like.
1097
- *
1098
- * AND THERE IS NO `cursor` RULE, which is not an omission. For the length of
1099
- * a native drag the browser draws the pointer from `dataTransfer.dropEffect`
1100
- * (the handlers set `'move'`); a `cursor` on the drop target is ignored
1101
- * throughout and would only mislead the next reader.
1102
- * ══════════════════════════════════════════════════════════════════════ */
1103
-
1104
- /* THE PANE UNDER THE POINTER. The preview rectangle says what a release
1105
- * would PRODUCE; this says which pane is being asked. An inset OUTLINE
1106
- * rather than a border, because a border changes the box and every tile in
1107
- * the row would reflow by a pixel as the pointer crossed between them. */
1108
- .twm-leaf--drop-target {
1109
- outline: 2px solid var(--accent-bright);
1110
- outline-offset: -2px;
1111
- }
1112
-
1113
- /* A STRIP ARMED TO RECEIVE — the drop that adds the tab at the end of this
1114
- * tile's list rather than into a zone of its body. */
1115
- .twm-leaf__tabbar--drop-target {
1116
- background: color-mix(in srgb, var(--accent-bright) 18%, transparent);
1117
- box-shadow: inset 0 0 0 1px var(--accent-bright);
1118
- }
1119
-
1120
- /* THE TAB BEING CARRIED. `DragReorder` has put `.dragging` on the TOP
1121
- * strip's tab since it was written and base.css dims it; the bottom strip's
1122
- * hand-rolled reorder set no class at all, so a drag there looked exactly
1123
- * like a click that did nothing. One declaration for both strips, so the two
1124
- * layouts cannot drift on what a dragged tab looks like. */
1125
- .twm-leaf__tab.dragging { opacity: 0.4; }
1126
-
1127
- .twm-leaf__tab-hamburger {
1128
- flex: 0 0 auto;
1129
- display: inline-flex;
1130
- align-items: center;
1131
- justify-content: center;
1132
- width: 22px;
1133
- height: 22px;
1134
- margin-right: 4px;
1135
- padding: 0;
1136
- background: transparent;
1137
- border: none;
1138
- color: var(--text-muted);
1139
- cursor: pointer;
1140
- border-radius: 0;
1141
- }
1142
- .twm-leaf__tab-hamburger:hover {
1143
- color: var(--text-primary);
1144
- background: var(--state-hover);
1145
- }
1146
- .twm-leaf__tabs {
1147
- list-style: none;
1148
- padding: 0;
1149
- margin: 0;
1150
- display: flex;
1151
- align-items: stretch;
1152
- flex: 1 1 auto;
1153
- min-width: 0;
1154
- overflow-x: auto;
1155
- scrollbar-width: none;
1156
- }
1157
- .twm-leaf__tabs::-webkit-scrollbar { display: none; }
1158
- .twm-leaf__tab {
1159
- /* Spreadsheet-style PARALLELOGRAM: both left and right edges
1160
- * slanted by ~8 px in the same direction (top-right / bottom-left
1161
- * pulled inward), giving each tab a uniform slant. Reads as the
1162
- * old Excel/Lotus sheet-tab look without the trapezoid's bottom
1163
- * flare. */
1164
- position: relative;
1165
- display: inline-flex;
1166
- align-items: center;
1167
- gap: 4px;
1168
- padding: 2px 14px 2px 14px;
1169
- /* Tabs sit flush against each other — no spacing between them.
1170
- * The parallelogram clip-path provides the visual separation on
1171
- * its own (each tab's left edge starts at 8 px in, the previous
1172
- * tab's right edge ends 8 px in; they interlock without overlap). */
1173
- margin-right: 0;
1174
- background: var(--surface-2);
1175
- color: var(--text-muted);
1176
- border: none;
1177
- cursor: pointer;
1178
- line-height: 1;
1179
- white-space: nowrap;
1180
- clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
1181
- }
1182
- .twm-leaf__tab:hover {
1183
- background: var(--surface-3);
1184
- color: var(--text-primary);
1185
- }
1186
- .twm-leaf__tab--on {
1187
- background: var(--surface-0); /* matches the body — looks lifted */
1188
- color: var(--text-bright);
1189
- /* Active tab gets a subtle border on the slanted edge so it
1190
- * separates from the surface-1 bar. Done via a layered ::before
1191
- * because clip-path drops the actual border. */
1192
- }
1193
- .twm-leaf__tab--on::before {
1194
- content: '';
1195
- position: absolute;
1196
- inset: 0;
1197
- pointer-events: none;
1198
- border-top: 1px solid var(--accent-bright);
1199
- clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
1200
- }
1201
- .twm-leaf__tab-label {
1202
- overflow: hidden;
1203
- text-overflow: ellipsis;
1204
- max-width: 160px;
1205
- text-transform: lowercase;
1206
- letter-spacing: 0.02em;
1207
- }
1208
- .twm-leaf__tab-close {
1209
- display: inline-flex;
1210
- align-items: center;
1211
- justify-content: center;
1212
- width: 14px;
1213
- height: 14px;
1214
- margin-left: 2px;
1215
- padding: 0;
1216
- background: transparent;
1217
- border: none;
1218
- color: inherit;
1219
- opacity: 0.6;
1220
- cursor: pointer;
1221
- border-radius: 0;
1222
- }
1223
- .twm-leaf__tab-close:hover {
1224
- opacity: 1;
1225
- color: var(--text-bright);
1226
- }
1227
- /* ── Tile tab menu (hamburger pop-out) ────────────────────────────
1228
- * Anchored above the hamburger button. Lists every entity that the
1229
- * tile's home page would show, grouped by source, with search +
1230
- * pagination. Click a row → opens in a new tab on the originating
1231
- * tile (see install._tileTabMenu). */
1232
- .twm-tile-tabmenu-overlay {
1233
- position: fixed;
1234
- inset: 0;
1235
- z-index: var(--z-modal);
1236
- pointer-events: none;
1237
- }
1238
- .twm-tile-tabmenu {
1239
- pointer-events: auto;
1240
- width: 360px;
1241
- max-height: 480px;
1242
- display: flex;
1243
- flex-direction: column;
1244
- background: var(--surface-1);
1245
- border: 1px solid var(--border-default);
1246
- box-shadow: var(--shadow-lg);
1247
- }
1248
- .twm-tile-tabmenu__head {
1249
- display: flex;
1250
- align-items: center;
1251
- gap: var(--space-sm);
1252
- padding: var(--space-sm) var(--space-md);
1253
- background: var(--surface-2);
1254
- border-bottom: 1px solid var(--border-subtle);
1255
- font-size: var(--font-size-xs);
1256
- text-transform: uppercase;
1257
- letter-spacing: 0.04em;
1258
- color: var(--text-secondary);
1259
- }
1260
- /* Compounded for the Material Symbols tie — see `.twm-leaf__icon`. */
1261
- .twm-tile-tabmenu__head-icon,
1262
- .twm-tile-tabmenu__head-icon.material-symbols-outlined { font-size: 14px; }
1263
- .twm-tile-tabmenu__head-label { flex: 1 1 auto; }
1264
- .twm-tile-tabmenu__close {
1265
- background: transparent;
1266
- border: none;
1267
- color: var(--text-muted);
1268
- cursor: pointer;
1269
- padding: 0 4px;
1270
- font-size: 14px;
1271
- }
1272
- .twm-tile-tabmenu__close:hover { color: var(--text-primary); }
1273
- .twm-tile-tabmenu__search {
1274
- display: flex;
1275
- align-items: center;
1276
- gap: var(--space-xs);
1277
- padding: 4px 8px;
1278
- border-bottom: 1px solid var(--border-subtle);
1279
- background: var(--surface-1);
1280
- }
1281
- .twm-tile-tabmenu__search input {
1282
- flex: 1 1 auto;
1283
- background: transparent;
1284
- border: none;
1285
- outline: none;
1286
- color: var(--text-primary);
1287
- font-size: var(--font-size-sm);
1288
- }
1289
- .twm-tile-tabmenu__body {
1290
- flex: 1 1 auto;
1291
- overflow-y: auto;
1292
- padding: 4px 0;
1293
- }
1294
- .twm-tile-tabmenu__loading {
1295
- padding: var(--space-md);
1296
- color: var(--text-muted);
1297
- font-size: var(--font-size-xs);
1298
- text-align: center;
1299
- }
1300
- .twm-tile-tabmenu__section {
1301
- padding: 0 0 4px;
1302
- border-bottom: 1px solid var(--border-subtle);
1303
- }
1304
- .twm-tile-tabmenu__section:last-child { border-bottom: none; }
1305
- .twm-tile-tabmenu__section-head {
1306
- display: flex;
1307
- align-items: center;
1308
- gap: var(--space-xs);
1309
- padding: 4px 8px;
1310
- color: var(--text-muted);
1311
- font-size: 10px;
1312
- text-transform: uppercase;
1313
- letter-spacing: 0.05em;
1314
- }
1315
- .twm-tile-tabmenu__section-label { flex: 1 1 auto; }
1316
- .twm-tile-tabmenu__section-count {
1317
- color: var(--text-muted);
1318
- background: var(--surface-2);
1319
- padding: 0 4px;
1320
- font-size: 9px;
1321
- }
1322
- .twm-tile-tabmenu__list {
1323
- list-style: none;
1324
- margin: 0;
1325
- padding: 0;
1326
- }
1327
- .twm-tile-tabmenu__item {
1328
- display: grid;
1329
- grid-template-columns: 1fr auto;
1330
- align-items: center;
1331
- gap: var(--space-sm);
1332
- padding: 3px 12px;
1333
- cursor: pointer;
1334
- color: var(--text-secondary);
1335
- font-size: var(--font-size-sm);
1336
- }
1337
- .twm-tile-tabmenu__item:hover,
1338
- .twm-tile-tabmenu__item--cursor {
1339
- background: var(--state-selected);
1340
- color: var(--text-bright);
1341
- }
1342
- .twm-tile-tabmenu__item--cursor {
1343
- /* Keyboard cursor reads slightly heavier than hover so a
1344
- * mouse-cursor-on-a-different-row state is still legible. */
1345
- outline: 1px solid var(--accent-bright);
1346
- outline-offset: -1px;
1347
- }
1348
- .twm-tile-tabmenu__item-hint {
1349
- color: var(--text-muted);
1350
- font-size: var(--font-size-xs);
1351
- }
1352
- .twm-tile-tabmenu__empty {
1353
- padding: 6px 12px;
1354
- color: var(--text-muted);
1355
- font-size: var(--font-size-xs);
1356
- }
1357
- .twm-tile-tabmenu__pager {
1358
- display: flex;
1359
- align-items: center;
1360
- justify-content: center;
1361
- gap: var(--space-md);
1362
- padding: 4px 8px;
1363
- color: var(--text-muted);
1364
- font-size: var(--font-size-xs);
1365
- }
1366
- .twm-tile-tabmenu__pager button {
1367
- background: transparent;
1368
- border: 1px solid var(--border-subtle);
1369
- color: inherit;
1370
- padding: 1px 6px;
1371
- cursor: pointer;
1372
- border-radius: 0;
1373
- }
1374
- .twm-tile-tabmenu__pager button:disabled {
1375
- opacity: 0.4;
1376
- cursor: not-allowed;
1377
- }
1378
-
1379
- /* ── from tiling/command_palette.css ───────────────────────── */
1380
- /* command_palette.css — Ctrl+K popup. */
1381
-
1382
- .twm-cmdpal-overlay {
1383
- position: fixed;
1384
- inset: 0;
1385
- z-index: var(--z-modal);
1386
- background: rgba(0, 0, 0, 0.55);
1387
- display: flex;
1388
- align-items: flex-start;
1389
- justify-content: center;
1390
- padding-top: 12vh;
1391
- }
1392
- .twm-cmdpal {
1393
- width: min(640px, 90vw);
1394
- background: var(--surface-1);
1395
- border: 1px solid var(--border-default);
1396
- border-radius: 0;
1397
- box-shadow: var(--shadow-lg);
1398
- overflow: hidden;
1399
- display: flex;
1400
- flex-direction: column;
1401
- }
1402
- /* Input row hosts the optional type-filter chip + the actual text
1403
- * input. The chip sits to the left of the caret; typing `<prefix>:`
1404
- * promotes the prefix into the chip and strips it from the input
1405
- * (see command_palette.js `tryConsumePrefix`). */
1406
- .twm-cmdpal__inputrow {
1407
- display: flex;
1408
- align-items: center;
1409
- gap: var(--space-sm);
1410
- padding: var(--space-md) var(--space-lg);
1411
- background: var(--surface-1);
1412
- border-bottom: 1px solid var(--border-subtle);
1413
- }
1414
- .twm-cmdpal__type-chip-slot:empty {
1415
- /* Empty slot collapses entirely so the input flushes against the
1416
- * row's left padding when no filter is set. */
1417
- display: none;
1418
- }
1419
- .twm-cmdpal__type-chip {
1420
- display: inline-flex;
1421
- align-items: center;
1422
- gap: var(--space-xs);
1423
- padding: 2px 4px 2px 8px;
1424
- background: var(--state-selected);
1425
- border: 1px solid var(--accent-bright);
1426
- border-radius: 2px;
1427
- color: var(--text-bright, #fff);
1428
- font-size: var(--font-size-xs);
1429
- white-space: nowrap;
1430
- user-select: none;
1431
- }
1432
- /* Compounded for the Material Symbols tie — see `.twm-leaf__icon`. */
1433
- .twm-cmdpal__type-chip-icon,
1434
- .twm-cmdpal__type-chip-icon.material-symbols-outlined {
1435
- font-size: 14px;
1436
- }
1437
- .twm-cmdpal__type-chip-label {
1438
- text-transform: lowercase;
1439
- letter-spacing: 0.02em;
1440
- }
1441
- .twm-cmdpal__type-chip-x {
1442
- display: inline-flex;
1443
- align-items: center;
1444
- justify-content: center;
1445
- width: 16px;
1446
- height: 16px;
1447
- margin-left: 2px;
1448
- background: transparent;
1449
- border: none;
1450
- color: inherit;
1451
- font-size: 14px;
1452
- line-height: 1;
1453
- cursor: pointer;
1454
- border-radius: 0;
1455
- }
1456
- .twm-cmdpal__type-chip-x:hover {
1457
- background: rgba(255, 255, 255, 0.12);
1458
- }
1459
- .twm-cmdpal__input {
1460
- flex: 1 1 auto;
1461
- min-width: 0;
1462
- padding: 0;
1463
- background: transparent;
1464
- border: none;
1465
- color: var(--text-primary);
1466
- font-size: var(--font-size-lg);
1467
- outline: none;
1468
- }
1469
- .twm-cmdpal__chips,
1470
- .twm-cmdpal__toggles {
1471
- display: flex;
1472
- flex-wrap: wrap;
1473
- gap: var(--space-xs);
1474
- padding: var(--space-sm) var(--space-md);
1475
- border-bottom: 1px solid var(--border-subtle);
1476
- }
1477
- .twm-chip {
1478
- display: inline-flex;
1479
- align-items: center;
1480
- gap: var(--space-xs);
1481
- padding: var(--space-xs) var(--space-sm);
1482
- background: var(--surface-2);
1483
- border: 1px solid var(--border-subtle);
1484
- border-radius: 0;
1485
- color: var(--text-secondary);
1486
- font-size: var(--font-size-xs);
1487
- cursor: pointer;
1488
- }
1489
- .twm-chip:hover {
1490
- background: var(--surface-3);
1491
- color: var(--text-primary);
1492
- }
1493
- .twm-chip--on {
1494
- background: var(--state-selected);
1495
- border-color: var(--accent-bright);
1496
- color: var(--text-bright);
1497
- }
1498
- .twm-chip[aria-disabled="true"] {
1499
- color: var(--text-dim);
1500
- cursor: not-allowed;
1501
- }
1502
- .twm-chip[aria-disabled="true"]:hover {
1503
- background: var(--surface-2);
1504
- color: var(--text-dim);
1505
- }
1506
- .twm-cmdpal__list {
1507
- max-height: 40vh;
1508
- overflow: auto;
1509
- }
1510
- .twm-cmdpal__item {
1511
- display: grid;
1512
- grid-template-columns: 20px 1fr auto auto;
1513
- align-items: center;
1514
- gap: var(--space-sm);
1515
- padding: var(--space-xs) var(--space-md);
1516
- cursor: pointer;
1517
- color: var(--text-secondary);
1518
- font-size: var(--font-size-sm);
1519
- }
1520
- .twm-cmdpal__item:hover,
1521
- .twm-cmdpal__item--active {
1522
- background: var(--state-selected);
1523
- color: var(--text-bright);
1524
- }
1525
- /* Compounded for the Material Symbols tie — see `.twm-leaf__icon`. */
1526
- .twm-cmdpal__icon,
1527
- .twm-cmdpal__icon.material-symbols-outlined { color: var(--text-muted); font-size: 16px; }
1528
- .twm-cmdpal__label { font-weight: var(--font-weight-medium); }
1529
- .twm-cmdpal__kind,
1530
- .twm-cmdpal__hint {
1531
- color: var(--text-muted);
1532
- font-size: var(--font-size-xs);
1533
- }
1534
- .twm-cmdpal__footer {
1535
- display: flex;
1536
- gap: var(--space-md);
1537
- padding: var(--space-xs) var(--space-md);
1538
- background: var(--surface-0);
1539
- border-top: 1px solid var(--border-subtle);
1540
- color: var(--text-muted);
1541
- font-size: var(--font-size-xs);
1542
- }
1543
- .twm-cmdpal__footer kbd {
1544
- display: inline-block;
1545
- padding: 1px 4px;
1546
- background: var(--surface-2);
1547
- border: 1px solid var(--border-subtle);
1548
- border-radius: 0;
1549
- font-family: var(--font-family-mono);
1550
- font-size: 10px;
1551
- margin: 0 1px;
1552
- }
1553
-
1554
- /* ── C39: the grouped layout (`layout: 'grouped'`) ───────────────────────
1555
- * Everything here is under `--grouped`, so the flat palette above draws
1556
- * exactly as it did. Rows are three columns: icon, the label with its
1557
- * context, and a right side that shows the kind — or, on the active row,
1558
- * the keys that act on it. */
1559
- .twm-cmdpal--grouped {
1560
- width: min(680px, calc(100vw - 32px));
1561
- max-height: 76vh;
1562
- border-color: var(--border-strong);
1563
- }
1564
- .twm-cmdpal-overlay--grouped { padding-top: 11vh; }
1565
- .twm-cmdpal--grouped .twm-cmdpal__inputrow {
1566
- padding: var(--space-md) var(--space-lg);
1567
- border-bottom-color: var(--border-default);
1568
- }
1569
- .twm-cmdpal__search-icon,
1570
- .twm-cmdpal__search-icon.material-symbols-outlined { color: var(--text-muted); font-size: 20px; }
1571
- .twm-cmdpal--grouped .twm-cmdpal__input { font-size: var(--font-size-xl); color: var(--text-bright); }
1572
- .twm-cmdpal--grouped .twm-cmdpal__input::placeholder { color: var(--text-muted); }
1573
- .twm-cmdpal--grouped .twm-cmdpal__type-chip-label--scope { text-transform: none; letter-spacing: 0; }
1574
- .twm-cmdpal__esc,
1575
- .twm-cmdpal--grouped kbd {
1576
- display: inline-block;
1577
- padding: 1px 4px;
1578
- background: var(--surface-2);
1579
- border: 1px solid var(--border-default);
1580
- border-radius: 0;
1581
- color: var(--text-secondary);
1582
- font-family: var(--font-family-mono);
1583
- font-size: 10px;
1584
- line-height: 1.3;
1585
- }
1586
- .twm-cmdpal--grouped .twm-cmdpal__list {
1587
- flex: 1 1 auto;
1588
- max-height: none;
1589
- min-height: 0;
1590
- padding: var(--space-xs) 0;
1591
- }
1592
- .twm-cmdpal__group {
1593
- padding: 10px var(--space-lg) 4px;
1594
- color: var(--text-muted);
1595
- font-size: var(--font-size-xs);
1596
- text-transform: uppercase;
1597
- letter-spacing: 0.06em;
1598
- }
1599
- .twm-cmdpal--grouped .twm-cmdpal__item {
1600
- grid-template-columns: 20px minmax(0, 1fr) auto;
1601
- gap: 10px;
1602
- padding: 5px var(--space-lg);
1603
- font-size: var(--font-size-md);
1604
- }
1605
- /* The flat layout highlights on :hover; here the pointer MOVES the active
1606
- * row instead, so a hover tint would be a second, disagreeing highlight. */
1607
- .twm-cmdpal--grouped .twm-cmdpal__item:hover:not(.twm-cmdpal__item--active) { background: transparent; color: var(--text-secondary); }
1608
- .twm-cmdpal--grouped .twm-cmdpal__item--active {
1609
- background: var(--state-selected);
1610
- color: var(--text-bright);
1611
- box-shadow: inset 2px 0 0 var(--accent-bright);
1612
- }
1613
- .twm-cmdpal--grouped .twm-cmdpal__item--active .twm-cmdpal__icon { color: var(--text-bright); }
1614
- .twm-cmdpal__main {
1615
- min-width: 0;
1616
- display: flex;
1617
- align-items: baseline;
1618
- gap: var(--space-sm);
1619
- }
1620
- .twm-cmdpal--grouped .twm-cmdpal__label {
1621
- font-weight: 400;
1622
- white-space: nowrap;
1623
- overflow: hidden;
1624
- text-overflow: ellipsis;
1625
- flex: 0 1 auto;
1626
- min-width: 0;
1627
- }
1628
- .twm-cmdpal__label mark {
1629
- background: none;
1630
- color: var(--twm-cmdpal-match);
1631
- font-weight: var(--font-weight-semibold);
1632
- }
1633
- .twm-cmdpal__context,
1634
- .twm-cmdpal__cells {
1635
- color: var(--text-muted);
1636
- font-size: var(--font-size-xs);
1637
- white-space: nowrap;
1638
- overflow: hidden;
1639
- text-overflow: ellipsis;
1640
- min-width: 0;
1641
- flex: 0 10 auto;
1642
- }
1643
- .twm-cmdpal__cells { font-family: var(--font-family-mono); }
1644
- .twm-cmdpal__side {
1645
- display: flex;
1646
- align-items: center;
1647
- gap: 10px;
1648
- color: var(--text-muted);
1649
- font-size: var(--font-size-xs);
1650
- white-space: nowrap;
1651
- }
1652
- .twm-cmdpal__acts { display: none; gap: 10px; }
1653
- .twm-cmdpal__item--active .twm-cmdpal__acts { display: flex; }
1654
- /* A copy verb a single kind refuses: drawn, greyed, with the reason as its title. */
1655
- .twm-cmdpal__act--disabled { opacity: 0.45; }
1656
- .twm-cmdpal__item--active .twm-cmdpal__kind,
1657
- .twm-cmdpal__item--active .twm-cmdpal__shortcut { display: none; }
1658
- .twm-cmdpal__item--more { color: var(--text-muted); font-size: var(--font-size-sm); }
1659
- .twm-cmdpal__empty {
1660
- padding: 24px var(--space-lg);
1661
- color: var(--text-muted);
1662
- text-align: center;
1663
- }
1664
- .twm-cmdpal__empty--quiet { padding: 6px var(--space-lg); text-align: left; font-size: var(--font-size-xs); }
1665
- .twm-cmdpal__tips {
1666
- display: flex;
1667
- flex-wrap: wrap;
1668
- gap: 6px 16px;
1669
- margin-top: var(--space-xs);
1670
- padding: 10px var(--space-lg) var(--space-sm);
1671
- border-top: 1px solid var(--border-subtle);
1672
- color: var(--text-muted);
1673
- font-size: var(--font-size-xs);
1674
- }
1675
- .twm-cmdpal--grouped .twm-cmdpal__footer { flex-wrap: wrap; row-gap: 2px; }
1676
- .twm-cmdpal__footnote { margin-left: auto; color: var(--text-muted); }
1677
- @media (max-width: 560px) {
1678
- .twm-cmdpal__context { display: none; }
1679
- .twm-cmdpal__acts > span:not(:first-child) { display: none; }
1680
- .twm-cmdpal__footnote { display: none; }
1681
- }
1682
-
1683
- /* ── from tiling/nav.css ───────────────────────── */
1684
- /* nav.css — left-nav panel-tile (project / files / git). */
1685
-
1686
- .twm-nav {
1687
- display: flex;
1688
- flex-direction: column;
1689
- height: 100%;
1690
- background: var(--surface-0);
1691
- overflow: hidden;
1692
- }
1693
- .twm-nav__modes {
1694
- display: flex;
1695
- gap: 0;
1696
- padding: 0;
1697
- background: var(--surface-1);
1698
- border-bottom: 1px solid var(--border-subtle);
1699
- }
1700
- .twm-nav__mode {
1701
- flex: 1 1 0;
1702
- display: inline-flex;
1703
- align-items: center;
1704
- justify-content: center;
1705
- gap: 4px;
1706
- padding: 6px 8px;
1707
- background: transparent;
1708
- border: none;
1709
- border-right: 1px solid var(--border-subtle);
1710
- border-bottom: 2px solid transparent;
1711
- border-radius: 0;
1712
- color: var(--text-secondary);
1713
- font-size: var(--font-size-xs);
1714
- cursor: pointer;
1715
- }
1716
- .twm-nav__mode:last-child { border-right: none; }
1717
- .twm-nav__mode:hover { background: var(--state-hover); }
1718
- .twm-nav__mode--on {
1719
- background: var(--surface-0);
1720
- color: var(--text-bright);
1721
- border-bottom-color: var(--accent-bright);
1722
- }
1723
- /* Filter strip — inherits look from sidebar.css's .twm-sidebar-controls
1724
- * primitive. Just tweak the bottom border to match our pane chrome. */
1725
- .twm-nav .twm-sidebar-controls.twm-nav__filter {
1726
- border-bottom: 1px solid var(--border-subtle);
1727
- }
1728
- .twm-nav__body {
1729
- flex: 1 1 auto;
1730
- overflow: hidden;
1731
- display: flex;
1732
- min-height: 0;
1733
- }
1734
- /* Each mode pane fills the body when visible. Inline style.display
1735
- * flips between 'flex' and 'none'. */
1736
- .twm-nav__pane {
1737
- flex: 1 1 auto;
1738
- width: 100%;
1739
- min-width: 0;
1740
- min-height: 0;
1741
- flex-direction: column;
1742
- overflow: hidden;
1743
- }
1744
- .twm-nav__table {
1745
- flex: 1 1 auto;
1746
- width: 100%;
1747
- overflow: auto;
1748
- min-height: 0;
1749
- }
1750
- /* Force the file-tree section's content to be expanded since there is
1751
- * no longer a header to toggle it. */
1752
- .twm-nav__pane--files .twm-collapsible-content {
1753
- display: block !important;
1754
- }
1755
- /* Action row above the file list — hosts the single "New file" button. */
1756
- .twm-nav__files-actions {
1757
- display: flex;
1758
- gap: 4px;
1759
- padding: 6px 6px 4px;
1760
- border-bottom: 1px solid var(--border-subtle);
1761
- flex: 0 0 auto;
1762
- }
1763
- .twm-nav__files-mount {
1764
- flex: 1 1 auto;
1765
- min-height: 0;
1766
- overflow: hidden;
1767
- display: flex;
1768
- flex-direction: column;
1769
- }
1770
- /* Reset <ul.twm-tree-view> browser defaults so list items render at the
1771
- * left edge of the body instead of with the 40px UA padding. */
1772
- .twm-nav__pane ul.twm-tree-view {
1773
- list-style: none;
1774
- margin: 0;
1775
- padding: 0;
1776
- }
1777
- .twm-nav__pane .twm-tree-item {
1778
- /* keep the .tree-item padding from main_new.css for nested chevron */
1779
- }
1780
- .twm-nav__icon {
1781
- font-size: 14px;
1782
- color: var(--text-muted);
1783
- vertical-align: middle;
1784
- }
1785
- .twm-nav__error {
1786
- padding: 12px;
1787
- color: var(--color-danger);
1788
- font-size: var(--font-size-xs);
1789
- }
1790
-
1791
- /* ── from tiling/host.css ───────────────────────── */
1792
- /* host.css — body layout for the tiling shell.
1793
- *
1794
- * The Ecosim bootstrap builds .twm-global-top-bar + .container + .twm-global-bottom-bar.
1795
- * Our installer removes .container and inserts .twm-host between the
1796
- * top and bottom bars; this stylesheet makes the body a flex column so
1797
- * the WM host fills the remaining space.
1798
- */
1799
-
1800
- /* Body is a flex column: top bar, menu row (when there is one), WM host
1801
- * (fill), bottom bar. We override the legacy `position: fixed` on both
1802
- * bars so they become
1803
- * deterministic flex items and the host's height is exactly the
1804
- * leftover space — no math, no overlap, no off-by-N pixels. Their
1805
- * `z-index` stays put for dropdown stacking. */
1806
- body.twm-body {
1807
- display: flex;
1808
- flex-direction: column;
1809
- margin: 0;
1810
- height: 100vh;
1811
- width: 100vw;
1812
- overflow: hidden;
1813
- }
1814
- body.twm-body > .twm-global-top-bar,
1815
- body.twm-body > .twm-global-bottom-bar {
1816
- position: relative !important;
1817
- inset: auto !important;
1818
- flex: 0 0 auto;
1819
- }
1820
- /* The menu row (STORY-0119) sits between the top bar and the host, in
1821
- * normal flow: a fixed-size strip, so the host's leftover space is less by
1822
- * exactly its height. */
1823
- body.twm-body > .twm-global-menu-bar {
1824
- flex: 0 0 auto;
1825
- }
1826
- body.twm-body > .twm-host {
1827
- flex: 1 1 auto;
1828
- min-height: 0;
1829
- min-width: 0;
1830
- display: flex;
1831
- overflow: hidden;
1832
- background: var(--surface-app);
1833
- }
1834
- /* Sim controls relocated into .twm-global-top-bar .twm-bar-right — they came
1835
- * from .workspace-top-bar where the layout was different. Strip the
1836
- * extra padding so they sit next to the panel-toggles cleanly. */
1837
- .twm-global-top-bar .twm-sim-controls {
1838
- display: inline-flex;
1839
- align-items: center;
1840
- gap: 4px;
1841
- margin-right: 8px;
1842
- padding: 0;
1843
- }
1844
- .twm-global-top-bar .twm-sim-controls .twm-menu-bar {
1845
- display: inline-flex;
1846
- align-items: center;
1847
- gap: 2px;
1848
- padding: 0;
1849
- background: transparent;
1850
- border: none;
1851
- }
1852
- .twm-global-top-bar .twm-sim-controls button {
1853
- background: transparent;
1854
- border: 1px solid transparent;
1855
- border-radius: 0;
1856
- color: var(--text-secondary);
1857
- }
1858
- .twm-global-top-bar .twm-sim-controls button:hover {
1859
- background: var(--state-hover);
1860
- color: var(--text-primary);
1861
- }
1862
- /* Desktop bar (mounted in global-bottom-bar .twm-bar-right). */
1863
- .twm-desktops {
1864
- display: inline-flex;
1865
- align-items: center;
1866
- gap: 2px;
1867
- margin: 0 8px;
1868
- }
1869
- .twm-desk__btn {
1870
- display: inline-flex;
1871
- align-items: center;
1872
- justify-content: center;
1873
- min-width: 22px;
1874
- height: 18px;
1875
- padding: 0 6px;
1876
- background: transparent;
1877
- border: 1px solid var(--border-subtle);
1878
- border-radius: 0;
1879
- color: var(--text-secondary);
1880
- font-family: var(--font-family-mono);
1881
- font-size: var(--font-size-xs);
1882
- cursor: pointer;
1883
- }
1884
- .twm-desk__btn:hover { background: var(--state-hover); }
1885
- .twm-desk__btn--on {
1886
- background: var(--state-selected);
1887
- color: var(--text-bright);
1888
- border-color: var(--accent-bright);
1889
- }
1890
- .twm-desk__btn--add { color: var(--text-muted); }
1891
- .twm-panel-toggle-btn--on {
1892
- background: var(--state-selected) !important;
1893
- color: var(--text-bright) !important;
1894
- }
1895
- /* Hamburger button — left of the EcoAgent brand. Collapses File/Edit/
1896
- * Help into a single popup so the top bar reads as one row of icons
1897
- * instead of three text dropdowns competing with the page shortcuts. */
1898
- .twm-hamburger-btn {
1899
- display: inline-flex;
1900
- align-items: center;
1901
- justify-content: center;
1902
- width: 35px;
1903
- height: 35px;
1904
- padding: 0;
1905
- background: transparent;
1906
- border: none;
1907
- border-radius: 0;
1908
- color: var(--text-secondary);
1909
- cursor: pointer;
1910
- }
1911
- .twm-hamburger-btn:hover {
1912
- background: var(--state-hover);
1913
- color: var(--text-primary);
1914
- }
1915
- /* Hide the original File/Edit/Help dropdowns — they live inside the
1916
- * hamburger menu now. Their `.menu-entry` buttons stay in the DOM so
1917
- * the hamburger can trigger them via .click(). */
1918
- .twm-global-top-bar .twm-menu-item.twm-hidden-menu-item {
1919
- display: none !important;
1920
- }
1921
- /* The agent + flow tabs each shipped their own "Pop out" button into a
1922
- * floating slide-out panel — superseded by the WM's Alt+F / promote-
1923
- * to-window. Hide them so the chrome doesn't carry two parallel
1924
- * affordances for the same action. */
1925
- /* Catch-all: any pop-out trigger inside any page tile is hidden. The
1926
- * tiling shell handles "pop a tile out into a floating window" via
1927
- * Alt+F / the chrome promote button, so per-page pop-out buttons are
1928
- * always redundant. */
1929
- .twm-page-shell [data-action="popout"],
1930
- .twm-page-shell #ea-archetype-popout,
1931
- .twm-page-shell #ea-sector-popout {
1932
- display: none !important;
1933
- }
1934
- /* Inline row-action buttons (edit / delete) on navigation tables. */
1935
- .twm-row-action {
1936
- display: inline-flex;
1937
- align-items: center;
1938
- justify-content: center;
1939
- width: 22px;
1940
- height: 22px;
1941
- padding: 0;
1942
- margin: 0 1px;
1943
- background: transparent;
1944
- border: 1px solid transparent;
1945
- border-radius: 0;
1946
- color: var(--text-muted);
1947
- cursor: pointer;
1948
- opacity: 0;
1949
- transition: opacity 80ms ease, color 80ms ease, background 80ms ease;
1950
- }
1951
- tr:hover .twm-row-action { opacity: 1; }
1952
- .twm-row-action:hover {
1953
- color: var(--text-primary);
1954
- background: var(--state-hover);
1955
- }
1956
- .twm-row-action--danger:hover {
1957
- color: var(--text-bright);
1958
- background: var(--color-danger);
1959
- }
1960
- .twm-row-actions-cell { white-space: nowrap; text-align: right; }
1961
- /* Hierarchical landing rows (Markets, Agents) — archetype rows carry
1962
- * a chevron + entity icon; instance rows are indented underneath. */
1963
- .twm-market-row {
1964
- display: inline-flex;
1965
- align-items: center;
1966
- gap: 6px;
1967
- width: 100%;
1968
- }
1969
- .twm-market-row__icon {
1970
- font-size: 14px;
1971
- color: var(--text-muted);
1972
- }
1973
- .twm-market-row--archetype .twm-market-row__icon {
1974
- color: var(--text-primary);
1975
- }
1976
- .twm-market-row__label {
1977
- flex: 1 1 auto;
1978
- min-width: 0;
1979
- overflow: hidden;
1980
- text-overflow: ellipsis;
1981
- white-space: nowrap;
1982
- }
1983
- .twm-market-chevron {
1984
- display: inline-flex;
1985
- align-items: center;
1986
- justify-content: center;
1987
- width: 18px;
1988
- height: 18px;
1989
- padding: 0;
1990
- background: transparent;
1991
- border: none;
1992
- color: var(--text-muted);
1993
- cursor: pointer;
1994
- }
1995
- .twm-market-chevron:hover { color: var(--text-primary); }
1996
- .twm-market-row--instance .twm-market-row__indent {
1997
- display: inline-block;
1998
- width: 22px;
1999
- flex: 0 0 22px;
2000
- }
2001
- .twm-market-row--instance .twm-market-row__label {
2002
- color: var(--text-secondary);
2003
- }
2004
- .twm-market-row__count {
2005
- display: inline-block;
2006
- margin-left: 6px;
2007
- padding: 0 5px;
2008
- color: var(--text-muted);
2009
- font-size: 11px;
2010
- border: 1px solid var(--border-subtle);
2011
- line-height: 1.5;
2012
- }
2013
- .twm-market-chevron:disabled {
2014
- opacity: 0.25;
2015
- cursor: default;
2016
- }
2017
- /* DataTable filter strip stickiness — the legacy rule for this lived
2018
- * only inside `.twm-preview-table-wrap--wizard`; generalize it so every
2019
- * DataTable (landings, instance lists, …) keeps its filter row + column
2020
- * headers visible when the body scrolls. Both rows need opaque
2021
- * backgrounds + z-index above the data cells so rows can't bleed
2022
- * through. */
2023
- /* Structural split: thead lives in its own non-scrolling header wrap
2024
- * above the scrollable body wrap. The header gets opaque chrome so it
2025
- * reads as a single bar with the body; no `position: sticky` is
2026
- * needed anymore since the header literally doesn't scroll. */
2027
- .twm-data-table-component .twm-preview-table-header-wrap {
2028
- background: var(--surface-1);
2029
- border-bottom: 1px solid var(--border-strong);
2030
- }
2031
- .twm-data-table-component .twm-preview-table-header-wrap .twm-preview-table thead th {
2032
- background: var(--surface-1);
2033
- }
2034
- .twm-data-table-component .twm-preview-table-header-wrap .twm-preview-table .twm-data-table__filter-row th {
2035
- background: var(--surface-0);
2036
- }
2037
- /* Compact mode (P2) — used by the bottom-panel Registries tab.
2038
- * Tighter padding + smaller font matches the surrounding bottom-panel
2039
- * chrome (run console, bus, query) and gets ~30% more rows visible
2040
- * in the same vertical space. Opt-in via `mode: 'compact'`. */
2041
- .twm-data-table-component--compact {
2042
- font-size: 11px;
2043
- }
2044
- .twm-data-table-component--compact .twm-preview-table th,
2045
- .twm-data-table-component--compact .twm-preview-table td {
2046
- /* 0.5.0: the row-geometry tokens (tokens.css), falling back to exactly
2047
- * the 0.4 compact values. */
2048
- padding: var(--twm-dt-cell-pad-block, 2px) var(--twm-dt-cell-pad-inline, 6px);
2049
- line-height: var(--twm-dt-cell-line-height, 1.35);
2050
- }
2051
- .twm-data-table-component--compact .twm-pagination-controls {
2052
- padding: 1px 6px !important;
2053
- font-size: 10px !important;
2054
- /* Tighter gap so pager reads as one strip, like the category /
2055
- * schema filter bar above the table — not as a spaced toolbar. */
2056
- gap: 4px !important;
2057
- background: #181818;
2058
- border-top: 1px solid var(--ea-border, #2a2a2a);
2059
- border-bottom: 1px solid var(--ea-border, #2a2a2a);
2060
- }
2061
- .twm-data-table-component--compact .twm-data-table__filter-input {
2062
- font-size: 10px;
2063
- padding: 1px 4px;
2064
- }
2065
- /* Match the narrow chrome of the filter strip (.ea-bp-browser__cat):
2066
- * 20px height, border-no-radius, transparent background, uppercase
2067
- * typography. So the pager reads as a sibling of the category /
2068
- * schema bar instead of a heavier toolbar.
2069
- *
2070
- * `!important` is used liberally below because DataTable.js still
2071
- * sets per-button / per-input chrome via inline `style.cssText` —
2072
- * inline beats CSS without `!important`. Once those inline styles
2073
- * migrate to CSS classes we can drop the `!important`s. */
2074
- .twm-data-table-component--compact .twm-pagination-controls .twm-pagination-btn {
2075
- min-width: 22px !important;
2076
- height: 20px !important;
2077
- padding: 0 6px !important;
2078
- background: transparent !important;
2079
- border: 1px solid var(--ea-border, #2a2a2a) !important;
2080
- border-radius: 0 !important;
2081
- color: var(--ea-text-dim, #aaa) !important;
2082
- font-size: 10px !important;
2083
- text-transform: uppercase;
2084
- letter-spacing: 0.04em;
2085
- }
2086
- .twm-data-table-component--compact .twm-pagination-controls .twm-pagination-btn:hover:not(:disabled) {
2087
- color: var(--ea-text, #ddd) !important;
2088
- border-color: var(--ea-accent) !important;
2089
- }
2090
- /* Text labels inside the pager — the "Rows X–Y of Z" and "Page X of
2091
- * Y" spans don't have classes (just inline style), so target every
2092
- * direct-span child of the controls to neutralise their inline color
2093
- * + font-size + add the uppercase look from the filter strip. */
2094
- /* Loading spinner — `setLoading(true)` (or returning a Promise from
2095
- * onPageChange/onSort) overlays a translucent layer + a CSS spinner
2096
- * over the whole table. Wrapper gets `position: relative` set
2097
- * inline by the JS so the absolute overlay anchors correctly. */
2098
- .twm-data-table__loading {
2099
- position: absolute;
2100
- inset: 0;
2101
- z-index: 10;
2102
- display: flex;
2103
- align-items: center;
2104
- justify-content: center;
2105
- /* Translucent so the user still sees the stale rows underneath
2106
- * and can recognise the table — the spinner says "fresh data
2107
- * coming", not "blank". */
2108
- background: rgba(20, 20, 20, 0.45);
2109
- backdrop-filter: blur(1px);
2110
- pointer-events: all; /* swallow clicks while in-flight */
2111
- cursor: progress;
2112
- }
2113
- .twm-data-table__spinner {
2114
- width: 28px;
2115
- height: 28px;
2116
- border: 2px solid rgba(255, 255, 255, 0.18);
2117
- border-top-color: var(--ea-accent);
2118
- border-radius: 50%;
2119
- animation: data-table__spin 0.7s linear infinite;
2120
- }
2121
- /* The keyframes the rule above names. Without them the name is no animation
2122
- (CSS reports nothing for it), so the ring was drawn and stood still. A full
2123
- turn at the rule's own 0.7s linear infinite. `@keyframes` names are global
2124
- and cannot be scoped (see `spin`); a definition of the same name in an
2125
- embedder's sheet wins or loses on source order, and either way the ring
2126
- turns. */
2127
- @keyframes data-table__spin {
2128
- from { transform: rotate(0deg); }
2129
- to { transform: rotate(360deg); }
2130
- }
2131
- .twm-data-table-component--compact .twm-pagination-controls > span {
2132
- color: var(--ea-text-dim, #aaa) !important;
2133
- font-size: 9.5px !important;
2134
- text-transform: uppercase;
2135
- letter-spacing: 0.04em;
2136
- }
2137
- /* Sticky-cell rules removed — header now lives in its own non-
2138
- * scrolling container after the structural split. */
2139
-
2140
- /* The legacy `.twm-landing__table` host has its own `overflow:auto`,
2141
- * which fights with DataTable's inner `.twm-preview-table-wrap` scroll
2142
- * container. When the outer scroll wins, the sticky filter row is
2143
- * computed against the wrong ancestor and stops sticking — rows
2144
- * scroll past the filter strip visually. Defer all scrolling to the
2145
- * inner wrap. Same fix for the navigator's table host. */
2146
- .twm-landing__table,
2147
- .twm-nav__table {
2148
- overflow: hidden !important;
2149
- }
2150
- .twm-landing__table > .twm-data-table-component,
2151
- .twm-nav__table > .twm-data-table-component {
2152
- flex: 1 1 auto;
2153
- min-height: 0;
2154
- }
2155
- /* main_new.css caps every `.twm-preview-table-wrap` at 300px so the legacy
2156
- * pre-tiling pages stayed compact. Inside the tiling shell, the wrap is
2157
- * the SOLE scrolling surface for the landing table — capping it at
2158
- * 300px clips the list to ~8 rows. Force the wrap to fill its flex
2159
- * parent and rely on its own `overflow: auto` (set inline by DataTable)
2160
- * for scrolling. */
2161
- .twm-landing__table .twm-preview-table-wrap,
2162
- .twm-nav__table .twm-preview-table-wrap {
2163
- max-height: none !important;
2164
- height: auto;
2165
- flex: 1 1 0;
2166
- min-height: 0;
2167
- }
2168
- /* Filter-row sticky rule removed — the row lives inside the non-
2169
- * scrolling header wrap after the structural split, so no sticky
2170
- * positioning is needed and the `top: 32px` magic-offset (which
2171
- * broke in compact mode) is gone. */
2172
-
2173
- /* Navigation-table density — tighten row padding for landings + the
2174
- * navigator. The default DataTable padding of `var(--space-sm) 8px` is
2175
- * comfortable for data-viewing tables; nav tables benefit from being
2176
- * denser so more items fit. */
2177
- .twm-landing .twm-data-table-component .twm-preview-table td,
2178
- .twm-landing .twm-data-table-component .twm-preview-table th,
2179
- .twm-nav__table .twm-data-table-component .twm-preview-table td,
2180
- .twm-nav__table .twm-data-table-component .twm-preview-table th {
2181
- padding: 2px 8px;
2182
- }
2183
- .twm-landing .twm-data-table-component .twm-preview-table .twm-data-table__filter-row th,
2184
- .twm-nav__table .twm-data-table-component .twm-preview-table .twm-data-table__filter-row th {
2185
- padding: 3px 4px;
2186
- /* Column header in these tables is ~22 px tall (font + 2×2 padding
2187
- * + 1 px border). The default filter-row `top: 32px` left a ~10 px
2188
- * gap rows scrolled through. Snap the filter row right up against
2189
- * the shorter header. */
2190
- top: 22px;
2191
- }
2192
- /* Breadcrumb strip — vertically center content. */
2193
- .twm-page-shell__breadcrumb {
2194
- display: flex;
2195
- align-items: center;
2196
- }
2197
- .twm-page-shell__breadcrumb .twm-tile-breadcrumb {
2198
- flex: 1 1 auto;
2199
- min-width: 0;
2200
- line-height: 1;
2201
- }
2202
- .twm-page-shell__breadcrumb .twm-topbar-breadcrumb__list {
2203
- display: inline-flex;
2204
- align-items: center;
2205
- margin: 0;
2206
- padding: 0;
2207
- list-style: none;
2208
- gap: 4px;
2209
- }
2210
- .twm-page-shell__breadcrumb .twm-topbar-breadcrumb__item,
2211
- .twm-page-shell__breadcrumb .twm-topbar-breadcrumb__link {
2212
- display: inline-flex;
2213
- align-items: center;
2214
- gap: 4px;
2215
- }
2216
- /* Modal dialog styling lives in ecoagent_extra_modes.css under
2217
- * `.twm-managed-window--modal`. Removed legacy in-host block — was a
2218
- * competing `display: flex; flex-direction: column` that stacked
2219
- * labels above fields and fought the canonical grid layout. */
2220
-
2221
- /* The Object Explorer's host expects `.panel.right .right-panel-body`
2222
- * — we recreate that shell inside the panel:right tile so the existing
2223
- * module mounts unchanged. The legacy `.panel.right` layout rules
2224
- * (fixed width, position, etc.) would fight the tile sizing, so we
2225
- * reset to a plain stretchy column. */
2226
- .twm-leaf__body .twm-panel-right {
2227
- position: static;
2228
- width: 100%;
2229
- height: 100%;
2230
- flex: 1 1 auto;
2231
- min-width: 0;
2232
- min-height: 0;
2233
- display: flex;
2234
- flex-direction: column;
2235
- background: transparent;
2236
- box-shadow: none;
2237
- overflow: hidden;
2238
- }
2239
- /* Page-shell: every content tile gets a breadcrumb strip on top and a
2240
- * scrollable content area below. */
2241
- .twm-page-shell {
2242
- display: flex;
2243
- flex-direction: column;
2244
- height: 100%;
2245
- overflow: hidden;
2246
- }
2247
- /* Lock the breadcrumb strip to a constant height across every page
2248
- * so the chrome doesn't visibly shift when switching between kinds
2249
- * with different segment counts. */
2250
- .twm-page-shell__breadcrumb {
2251
- flex: 0 0 26px;
2252
- min-height: 26px;
2253
- max-height: 26px;
2254
- border-bottom: 1px solid var(--border-subtle);
2255
- background: var(--surface-1);
2256
- overflow: hidden;
2257
- }
2258
- .twm-page-shell__breadcrumb .twm-tile-breadcrumb {
2259
- display: flex;
2260
- align-items: center;
2261
- gap: 6px;
2262
- padding: 2px 8px;
2263
- line-height: 22px;
2264
- }
2265
- /* A path of one segment is not drawn (STORY-0124): the tile's title already
2266
- * names it, so the strip and its slot take no room until a second level. */
2267
- .twm-page-shell__breadcrumb .twm-tile-breadcrumb[hidden],
2268
- .twm-page-shell__breadcrumb:has(> .twm-tile-breadcrumb[hidden]) {
2269
- display: none;
2270
- }
2271
- .twm-page-shell__content {
2272
- flex: 1 1 auto;
2273
- min-height: 0;
2274
- min-width: 0;
2275
- /* No `overflow: auto` here — each mounted page owns its own
2276
- * scroll region (e.g. .ea-agent-editor's `.nb-structured-editor__content`).
2277
- * Allowing the wrapper to scroll on top of that produced double
2278
- * scrollbars + a shimmy on sub-tab switches inside agent / market
2279
- * pages (Settings vs code). */
2280
- overflow: hidden;
2281
- display: flex;
2282
- flex-direction: column;
2283
- /* Children that need to layer over the content slot (the loading
2284
- * overlay) anchor against this. */
2285
- position: relative;
2286
- }
2287
- /* Loading overlay — shown while a page's async mount() is in flight.
2288
- * `--pending` is the pre-show state set for the first 80ms so cache
2289
- * hits / instant renders never strobe a spinner. After that the
2290
- * overlay becomes visible until the mount promise resolves. */
2291
- .twm-tile-loading {
2292
- position: absolute;
2293
- inset: 0;
2294
- display: flex;
2295
- align-items: center;
2296
- justify-content: center;
2297
- background: var(--ea-bg-content, #1a1a1a);
2298
- z-index: 5;
2299
- opacity: 1;
2300
- transition: opacity 120ms ease-out;
2301
- }
2302
- .twm-tile-loading--pending {
2303
- opacity: 0;
2304
- pointer-events: none;
2305
- }
2306
- .twm-tile-loading__spinner {
2307
- width: 18px;
2308
- height: 18px;
2309
- border: 2px solid var(--ea-border, #333);
2310
- border-top-color: var(--accent);
2311
- border-radius: 50%;
2312
- animation: twm-tile-loading-spin 0.7s linear infinite;
2313
- }
2314
- /* The keyframes the rule above names; see `data-table__spin`. */
2315
- @keyframes twm-tile-loading-spin {
2316
- from { transform: rotate(0deg); }
2317
- to { transform: rotate(360deg); }
2318
- }
2319
- /* Merged home page — detail header row, then the project-setup body
2320
- * fills the rest of the tile. */
2321
- .twm-home-page {
2322
- display: flex;
2323
- flex-direction: column;
2324
- height: 100%;
2325
- overflow: hidden;
2326
- }
2327
- .twm-home-page__header {
2328
- flex: 0 0 auto;
2329
- }
2330
- .twm-home-page__body {
2331
- flex: 1 1 auto;
2332
- min-height: 0;
2333
- overflow: auto;
2334
- display: flex;
2335
- flex-direction: column;
2336
- }
2337
- /* Detail header — uniform height + padding across every tab. */
2338
- .twm-page-shell__content .twm-entity-header {
2339
- min-height: 32px;
2340
- padding: 4px 12px !important;
2341
- margin: 0 !important;
2342
- background: var(--surface-1);
2343
- border-bottom: 1px solid var(--border-subtle);
2344
- }
2345
- /* Bottom panel mounted as a tile — reset the legacy fixed/absolute
2346
- * layout `.left-bottom` carries from main_new.css so it stretches to
2347
- * fill the tile body. */
2348
- .twm-leaf__body .twm-panel-bottom {
2349
- position: static;
2350
- height: 100%;
2351
- width: 100%;
2352
- display: flex;
2353
- flex-direction: column;
2354
- overflow: hidden;
2355
- }
2356
- .twm-leaf__body .twm-panel-bottom #bottom-tabs-content {
2357
- flex: 1 1 auto;
2358
- min-height: 0;
2359
- overflow: auto;
2360
- }
2361
- .twm-bp-loading {
2362
- padding: 14px;
2363
- color: var(--text-muted);
2364
- font-size: var(--font-size-xs);
2365
- }
2366
- .twm-bp-loading--err { color: var(--color-danger); }
2367
- /* Self-contained bottom panel — sole owner of the panel:bottom tile.
2368
- * Tab strip on top, body below; both painted synchronously by the
2369
- * tiling shell so the panel can never end up visually empty. */
2370
- .twm-bp {
2371
- display: flex;
2372
- flex-direction: column;
2373
- height: 100%;
2374
- width: 100%;
2375
- overflow: hidden;
2376
- background: var(--surface-0);
2377
- color: var(--text-body);
2378
- }
2379
- /* Tab strip matches the legacy `.bottom-tabs-header` look: light row
2380
- * with no per-tab background, uppercase labels spaced by left margin,
2381
- * white underline on the active tab. No boxy buttons. */
2382
- .twm-bp__tabs {
2383
- flex: 0 0 auto;
2384
- display: flex;
2385
- align-items: center;
2386
- gap: 0;
2387
- padding: 0 0 0 4px;
2388
- background: var(--surface-0);
2389
- border-bottom: 1px solid #292929;
2390
- overflow-x: auto;
2391
- overflow-y: hidden;
2392
- scrollbar-width: none;
2393
- white-space: nowrap;
2394
- }
2395
- .twm-bp__tabs::-webkit-scrollbar { width: 0; height: 0; display: none; }
2396
- .twm-bp__tab {
2397
- display: inline-flex;
2398
- align-items: center;
2399
- gap: 0;
2400
- height: 30px;
2401
- line-height: 30px;
2402
- margin-left: 10px;
2403
- padding: 0;
2404
- background: transparent;
2405
- border: none;
2406
- border-bottom: 1px solid transparent;
2407
- color: inherit;
2408
- text-transform: uppercase;
2409
- font-size: var(--font-size-xs);
2410
- cursor: pointer;
2411
- user-select: none;
2412
- }
2413
- .twm-bp__tab:hover { background: transparent; color: var(--text-bright); }
2414
- .twm-bp__tab--on {
2415
- color: var(--text-bright);
2416
- border-bottom-color: #fff;
2417
- }
2418
- .twm-bp__body {
2419
- flex: 1 1 auto;
2420
- min-height: 0;
2421
- overflow: auto;
2422
- }
2423
- .twm-bp-pane {
2424
- padding: 6px 10px;
2425
- }
2426
- .twm-bp-pane__placeholder {
2427
- padding: 12px;
2428
- color: var(--text-muted);
2429
- font-size: var(--font-size-xs);
2430
- }
2431
- .twm-bp-log {
2432
- width: 100%;
2433
- border-collapse: collapse;
2434
- font-family: var(--font-family-mono);
2435
- font-size: var(--font-size-xs);
2436
- }
2437
- .twm-bp-log thead th {
2438
- text-align: left;
2439
- color: var(--text-muted);
2440
- padding: 4px 8px;
2441
- border-bottom: 1px solid var(--border-subtle);
2442
- text-transform: uppercase;
2443
- letter-spacing: 0.04em;
2444
- font-size: 10px;
2445
- font-weight: 600;
2446
- }
2447
- .twm-bp-log td {
2448
- padding: 2px 8px;
2449
- border-bottom: 1px solid var(--border-subtle);
2450
- vertical-align: top;
2451
- }
2452
- .twm-bp-log__tick { color: var(--text-muted); white-space: nowrap; }
2453
- .twm-bp-log__level {
2454
- color: var(--text-secondary);
2455
- text-transform: lowercase;
2456
- white-space: nowrap;
2457
- }
2458
- .twm-bp-log__row--warn .twm-bp-log__level,
2459
- .twm-bp-log__row--warn .twm-bp-log__msg { color: var(--color-warning); }
2460
- .twm-bp-log__row--error .twm-bp-log__level,
2461
- .twm-bp-log__row--error .twm-bp-log__msg { color: var(--color-danger); }
2462
- .twm-bp-log__msg { color: var(--text-body); white-space: pre-wrap; word-break: break-word; }
2463
- /* Context menu — override main_new.css's .twm-context-menu to the tighter,
2464
- * grid-style look (icon | label) with a flat surface-1 background and
2465
- * square corners. All callers (sidebars, tile chrome, desktop bar, …)
2466
- * inherit this look — one implementation, one style. */
2467
- .twm-context-menu {
2468
- background: var(--surface-1);
2469
- border: 1px solid var(--border-default);
2470
- border-radius: 0;
2471
- box-shadow: var(--shadow-md);
2472
- padding: 4px 0;
2473
- min-width: 200px;
2474
- backdrop-filter: none;
2475
- }
2476
- .twm-context-menu-item {
2477
- display: grid;
2478
- grid-template-columns: 22px 1fr;
2479
- align-items: center;
2480
- gap: 8px;
2481
- padding: 4px 10px;
2482
- font-family: var(--font-family-sans);
2483
- font-size: var(--font-size-sm);
2484
- color: var(--text-primary);
2485
- }
2486
- .twm-context-menu-item.twm-delete-node {
2487
- color: var(--color-danger);
2488
- }
2489
- .twm-context-menu__separator {
2490
- height: 1px;
2491
- margin: 4px 0;
2492
- background: var(--border-subtle);
2493
- }
2494
- /* Window-placeholder body — shown in the leaf while its content is in
2495
- * a managed window. */
2496
- .twm-window-placeholder {
2497
- height: 100%;
2498
- display: flex;
2499
- flex-direction: column;
2500
- align-items: center;
2501
- justify-content: center;
2502
- gap: 8px;
2503
- padding: 24px;
2504
- color: var(--text-muted);
2505
- text-align: center;
2506
- }
2507
- /* Compounded for the Material Symbols tie — see `.twm-leaf__icon`. */
2508
- .twm-window-placeholder__icon,
2509
- .twm-window-placeholder__icon.material-symbols-outlined {
2510
- font-size: 32px;
2511
- opacity: 0.5;
2512
- }
2513
- .twm-window-placeholder__title {
2514
- color: var(--text-primary);
2515
- font-weight: var(--font-weight-semibold);
2516
- }
2517
- .twm-window-placeholder__hint {
2518
- font-size: var(--font-size-xs);
2519
- }
2520
- .twm-window-placeholder__btn {
2521
- margin-top: 8px;
2522
- padding: 4px 12px;
2523
- background: var(--surface-2);
2524
- border: 1px solid var(--border-subtle);
2525
- border-radius: 0;
2526
- color: var(--text-primary);
2527
- cursor: pointer;
2528
- }
2529
- .twm-window-placeholder__btn:hover {
2530
- background: var(--state-selected);
2531
- color: var(--text-bright);
2532
- border-color: var(--accent-bright);
2533
- }
2534
- /* Anchor bar-center next to bar-left (instead of centred). bar-right
2535
- * gets pushed against the right edge with margin-left:auto so the
2536
- * existing justify-content: space-between still reads correctly. */
2537
- .twm-global-top-bar .twm-bar-left { flex: 0 0 auto; }
2538
- .twm-global-top-bar .twm-bar-right { flex: 0 0 auto; margin-left: auto; }
2539
- /* Top-nav buttons in .twm-bar-center — archaic console look: full-height,
2540
- * monospace label, square, dark active-bg with white text. Each
2541
- * button is only as wide as its content; the strip takes only as
2542
- * much horizontal space as required. */
2543
- .twm-global-top-bar .twm-bar-center.twm-top-nav {
2544
- display: flex;
2545
- align-items: stretch;
2546
- align-self: stretch;
2547
- gap: 0;
2548
- padding: 0;
2549
- flex: 0 0 auto;
2550
- height: 100%;
2551
- min-width: 0;
2552
- }
2553
- .twm-top-nav__btn {
2554
- display: inline-flex;
2555
- align-items: center;
2556
- justify-content: center;
2557
- gap: 6px;
2558
- flex: 0 0 auto;
2559
- min-width: 0;
2560
- height: 100%;
2561
- padding: 0 14px;
2562
- background: transparent;
2563
- border: none;
2564
- border-right: 1px solid var(--surface-0);
2565
- border-radius: 0;
2566
- color: var(--text-muted);
2567
- font-family: var(--font-family-mono);
2568
- font-size: 11px;
2569
- font-weight: var(--font-weight-medium);
2570
- text-transform: uppercase;
2571
- letter-spacing: 0.08em;
2572
- cursor: pointer;
2573
- transition: color 80ms ease, background 80ms ease;
2574
- }
2575
- .twm-top-nav__btn:last-child { border-right: none; }
2576
- .twm-top-nav__btn:hover {
2577
- background: rgba(255, 255, 255, 0.04);
2578
- color: var(--text-primary);
2579
- }
2580
- .twm-top-nav__btn--on {
2581
- color: var(--text-bright);
2582
- background: var(--surface-well);
2583
- }
2584
- .twm-top-nav__btn--on:hover {
2585
- background: #000;
2586
- color: var(--text-bright);
2587
- }
2588
- /* Compounded for the Material Symbols tie — see `.twm-leaf__icon`. */
2589
- .twm-top-nav__icon,
2590
- .twm-top-nav__icon.material-symbols-outlined {
2591
- font-size: 16px;
2592
- font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;
2593
- }
2594
- .twm-top-nav__label {
2595
- /* hidden on narrow viewports; icon alone is enough */
2596
- }