flexdesk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/THIRD_PARTY_LICENSES.md +53 -0
- package/css/base.css +3439 -0
- package/css/flexdesk.css +5797 -0
- package/css/overrides.css +2349 -0
- package/css/reset.css +46 -0
- package/css/tokens.css +137 -0
- package/dist/charts.js +780 -0
- package/dist/charts.js.map +7 -0
- package/dist/chunk-3PHCPZHT.js +222 -0
- package/dist/chunk-3PHCPZHT.js.map +7 -0
- package/dist/chunk-CT4YXXLP.js +1770 -0
- package/dist/chunk-CT4YXXLP.js.map +7 -0
- package/dist/chunk-DRYCDMEG.js +2217 -0
- package/dist/chunk-DRYCDMEG.js.map +7 -0
- package/dist/chunk-DVU44T77.js +855 -0
- package/dist/chunk-DVU44T77.js.map +7 -0
- package/dist/chunk-FL5KFNQH.js +640 -0
- package/dist/chunk-FL5KFNQH.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +117 -0
- package/dist/chunk-FOOS3T5L.js.map +7 -0
- package/dist/chunk-JYWURG5T.js +32 -0
- package/dist/chunk-JYWURG5T.js.map +7 -0
- package/dist/chunk-TLZUUFOE.js +276 -0
- package/dist/chunk-TLZUUFOE.js.map +7 -0
- package/dist/chunk-UCJ2WD4D.js +625 -0
- package/dist/chunk-UCJ2WD4D.js.map +7 -0
- package/dist/chunk-WVFGV5FT.js +205 -0
- package/dist/chunk-WVFGV5FT.js.map +7 -0
- package/dist/core.js +1098 -0
- package/dist/core.js.map +7 -0
- package/dist/editor.js +1661 -0
- package/dist/editor.js.map +7 -0
- package/dist/flexdesk.css +5797 -0
- package/dist/host.js +132 -0
- package/dist/host.js.map +7 -0
- package/dist/reset.css +46 -0
- package/dist/tile_registry-6WZPTUZV.js +28 -0
- package/dist/tile_registry-6WZPTUZV.js.map +7 -0
- package/dist/tiles.js +2068 -0
- package/dist/tiles.js.map +7 -0
- package/dist/tokens.css +137 -0
- package/dist/widgets.js +5438 -0
- package/dist/widgets.js.map +7 -0
- package/dist/wm.js +4830 -0
- package/dist/wm.js.map +7 -0
- package/package.json +80 -0
- package/src/charts/chart_types.js +699 -0
- package/src/charts/data_series_plot_window.js +761 -0
- package/src/charts/downsample.js +119 -0
- package/src/charts/plot_config.js +176 -0
- package/src/charts/plot_popout_window.js +1560 -0
- package/src/charts/plotly_wrapper.js +432 -0
- package/src/core/event_bus.js +230 -0
- package/src/core/logging.js +300 -0
- package/src/core/settings.js +813 -0
- package/src/core/state_guard.js +382 -0
- package/src/core/state_guard_config.js +309 -0
- package/src/core/state_machine.js +258 -0
- package/src/editor/editor_pane.js +264 -0
- package/src/editor/monaco_editor_factory.js +449 -0
- package/src/editor/monaco_loader.js +74 -0
- package/src/editor/notebook_search.js +460 -0
- package/src/editor/notebook_tab_bar.js +456 -0
- package/src/editor/notebook_undo_manager.js +108 -0
- package/src/editor/split_pane_container.js +197 -0
- package/src/help/help_modal.js +409 -0
- package/src/help/help_registry.js +65 -0
- package/src/host/host.js +69 -0
- package/src/host/pywebview_host.js +133 -0
- package/src/tiles/config_schema.js +256 -0
- package/src/tiles/layout_persistence.js +662 -0
- package/src/tiles/tile_base.js +785 -0
- package/src/tiles/tile_grid.js +905 -0
- package/src/tiles/tile_registry.js +136 -0
- package/src/tiling/command_palette.js +328 -0
- package/src/tiling/content_registry.js +110 -0
- package/src/tiling/desktops.js +133 -0
- package/src/tiling/entity_sources.js +179 -0
- package/src/tiling/keymap.js +156 -0
- package/src/tiling/kind_taxonomy.js +172 -0
- package/src/tiling/landing_table.js +717 -0
- package/src/tiling/loading_overlay.js +33 -0
- package/src/tiling/nav_panel.js +142 -0
- package/src/tiling/page_factory.js +215 -0
- package/src/tiling/panel_keys.js +145 -0
- package/src/tiling/shell.js +437 -0
- package/src/tiling/tile_breadcrumb.js +222 -0
- package/src/tiling/tile_renderer.js +459 -0
- package/src/tiling/tile_tab_menu.js +340 -0
- package/src/tiling/tile_tree.js +880 -0
- package/src/tiling/wm.js +1352 -0
- package/src/ui/base/component_base.js +30 -0
- package/src/ui/base/controller_base.js +48 -0
- package/src/ui/base/page_base.js +102 -0
- package/src/ui/components/about_dialog.js +224 -0
- package/src/ui/components/action_dropdown.js +389 -0
- package/src/ui/components/attribute_list_editor.js +652 -0
- package/src/ui/components/autocomplete_field.js +732 -0
- package/src/ui/components/computing_status_window.js +81 -0
- package/src/ui/components/confirm_dialog.js +307 -0
- package/src/ui/components/context_menu.js +87 -0
- package/src/ui/components/data_table.js +2228 -0
- package/src/ui/components/detail_header.js +177 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/gallery_picker.js +147 -0
- package/src/ui/components/inline_renamer.js +129 -0
- package/src/ui/components/managed_window.js +804 -0
- package/src/ui/components/modal.js +512 -0
- package/src/ui/components/notification_history.js +264 -0
- package/src/ui/components/slide_out_panel.js +185 -0
- package/src/ui/components/slider_field.js +122 -0
- package/src/ui/components/sortable_list.js +309 -0
- package/src/ui/components/table_state_store.js +81 -0
- package/src/ui/components/toast.js +59 -0
- package/src/ui/components/tree_view.js +460 -0
- package/src/ui/components/window_taskbar.js +143 -0
- package/src/ui/controllers/panel_state_machine.js +634 -0
- package/src/ui/controllers/window_chrome_controller.js +659 -0
- package/src/ui/notification_center.js +576 -0
- package/src/ui/utils/overlay_scrollbar.js +492 -0
- package/src/ui/utils/raf_resize_observer.js +31 -0
- package/src/ui/utils/tooltip_service.js +406 -0
|
@@ -0,0 +1,2349 @@
|
|
|
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: #1486cf;
|
|
134
|
+
border-color: #1486cf;
|
|
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). */
|
|
245
|
+
.twm-managed-window--modal .twm-managed-window__content {
|
|
246
|
+
background: var(--ea-bg-content);
|
|
247
|
+
padding: 0;
|
|
248
|
+
overflow: auto;
|
|
249
|
+
}
|
|
250
|
+
.twm-managed-window--modal .twm-modal__body-host {
|
|
251
|
+
display: flex;
|
|
252
|
+
flex-direction: column;
|
|
253
|
+
min-height: 100%;
|
|
254
|
+
}
|
|
255
|
+
/* Form sits inside the body host as a flex column so the actions row
|
|
256
|
+
* gets pushed to the bottom via margin-top:auto. */
|
|
257
|
+
.twm-managed-window--modal .twm-modal__form {
|
|
258
|
+
display: flex;
|
|
259
|
+
flex-direction: column;
|
|
260
|
+
flex: 1 1 auto;
|
|
261
|
+
padding: 0;
|
|
262
|
+
}
|
|
263
|
+
/* Field rows get inset margins so the action strip can run edge-to-edge
|
|
264
|
+
* as a true bottom band. */
|
|
265
|
+
.twm-managed-window--modal .twm-modal__form > .twm-modal__row,
|
|
266
|
+
.twm-managed-window--modal .twm-modal__form > .twm-modal__section,
|
|
267
|
+
.twm-managed-window--modal .twm-modal__form > .twm-modal__error,
|
|
268
|
+
.twm-managed-window--modal .twm-modal__body {
|
|
269
|
+
margin-left: 14px;
|
|
270
|
+
margin-right: 14px;
|
|
271
|
+
}
|
|
272
|
+
.twm-managed-window--modal .twm-modal__form > .twm-modal__row:first-of-type,
|
|
273
|
+
.twm-managed-window--modal .twm-modal__form > .twm-modal__section:first-child,
|
|
274
|
+
.twm-managed-window--modal .twm-modal__body:first-child {
|
|
275
|
+
margin-top: 14px;
|
|
276
|
+
}
|
|
277
|
+
/* Rich-content body (openModal). Fills the available height between
|
|
278
|
+
* the title strip and the action footer; the caller's content sits
|
|
279
|
+
* inside and inherits the scroll. */
|
|
280
|
+
.twm-managed-window--modal .twm-modal__body--rich {
|
|
281
|
+
margin: 0 14px 0;
|
|
282
|
+
padding: 0;
|
|
283
|
+
flex: 1 1 auto;
|
|
284
|
+
min-height: 0;
|
|
285
|
+
overflow: auto;
|
|
286
|
+
display: flex;
|
|
287
|
+
flex-direction: column;
|
|
288
|
+
}
|
|
289
|
+
.twm-managed-window--modal .twm-modal__body--rich > * {
|
|
290
|
+
flex: 1 1 auto;
|
|
291
|
+
min-height: 0;
|
|
292
|
+
}
|
|
293
|
+
/* Fielded input mask: label right-aligned with a colon suffix, input
|
|
294
|
+
* occupying the remaining width. Mirrors how curses/dialog renders a
|
|
295
|
+
* form — labels read down the left edge, values down the right. */
|
|
296
|
+
.twm-managed-window--modal .twm-modal__row {
|
|
297
|
+
display: grid;
|
|
298
|
+
grid-template-columns: 130px 1fr;
|
|
299
|
+
align-items: center;
|
|
300
|
+
column-gap: 10px;
|
|
301
|
+
row-gap: 0;
|
|
302
|
+
margin-bottom: 10px;
|
|
303
|
+
}
|
|
304
|
+
.twm-managed-window--modal .twm-modal__row--multiline {
|
|
305
|
+
align-items: start;
|
|
306
|
+
}
|
|
307
|
+
.twm-managed-window--modal .twm-modal__row > span:first-child {
|
|
308
|
+
color: var(--ea-text-dim);
|
|
309
|
+
font-size: var(--ea-font-body);
|
|
310
|
+
text-transform: uppercase;
|
|
311
|
+
letter-spacing: 0.05em;
|
|
312
|
+
text-align: right;
|
|
313
|
+
justify-self: end;
|
|
314
|
+
line-height: var(--ea-row-h);
|
|
315
|
+
}
|
|
316
|
+
/* Add the trailing colon via CSS so callers don't have to. Suppress on
|
|
317
|
+
* checkbox rows (their left cell is intentionally empty). */
|
|
318
|
+
.twm-managed-window--modal .twm-modal__row > span:first-child:not(:empty)::after {
|
|
319
|
+
content: ' :';
|
|
320
|
+
color: var(--ea-text-muted);
|
|
321
|
+
margin-left: 1px;
|
|
322
|
+
}
|
|
323
|
+
.twm-managed-window--modal .twm-modal__row.twm-is-invalid > span:first-child {
|
|
324
|
+
color: #f0a8a8;
|
|
325
|
+
}
|
|
326
|
+
/* Input wells — deeper than the body so the field reads as "the place
|
|
327
|
+
* you type" against the panel. Sharp 1 px border with a 1 px inner
|
|
328
|
+
* shadow that simulates being inset. */
|
|
329
|
+
.twm-managed-window--modal .twm-modal__row input[type="text"],
|
|
330
|
+
.twm-managed-window--modal .twm-modal__row input[type="number"],
|
|
331
|
+
.twm-managed-window--modal .twm-modal__row input[type="password"],
|
|
332
|
+
.twm-managed-window--modal .twm-modal__row input[type="search"],
|
|
333
|
+
.twm-managed-window--modal .twm-modal__row input[type="email"],
|
|
334
|
+
.twm-managed-window--modal .twm-modal__row select,
|
|
335
|
+
.twm-managed-window--modal .twm-modal__row textarea {
|
|
336
|
+
background: #0a0a0a;
|
|
337
|
+
color: var(--ea-text);
|
|
338
|
+
border: 1px solid #3a3a3a;
|
|
339
|
+
border-radius: 0;
|
|
340
|
+
/* Shared input padding (same token as `.ea-tin`). Was `0 8px` with a
|
|
341
|
+
* line-height fudge for vertical centering — real padding instead. */
|
|
342
|
+
padding: var(--ea-input-pad);
|
|
343
|
+
min-height: 22px;
|
|
344
|
+
line-height: normal;
|
|
345
|
+
font: inherit;
|
|
346
|
+
font-size: var(--ea-font-body);
|
|
347
|
+
width: 100%;
|
|
348
|
+
box-sizing: border-box;
|
|
349
|
+
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.4) inset;
|
|
350
|
+
}
|
|
351
|
+
.twm-managed-window--modal .twm-modal__row textarea {
|
|
352
|
+
line-height: 1.45;
|
|
353
|
+
padding: 6px 8px;
|
|
354
|
+
min-height: calc(var(--ea-row-h) * 4);
|
|
355
|
+
resize: vertical;
|
|
356
|
+
}
|
|
357
|
+
.twm-managed-window--modal .twm-modal__row input:focus,
|
|
358
|
+
.twm-managed-window--modal .twm-modal__row select:focus,
|
|
359
|
+
.twm-managed-window--modal .twm-modal__row textarea:focus {
|
|
360
|
+
outline: none;
|
|
361
|
+
border-color: var(--ea-accent);
|
|
362
|
+
box-shadow:
|
|
363
|
+
0 1px 0 rgba(0, 0, 0, 0.4) inset,
|
|
364
|
+
0 0 0 1px var(--ea-accent);
|
|
365
|
+
}
|
|
366
|
+
.twm-managed-window--modal .twm-modal__row.twm-is-invalid input,
|
|
367
|
+
.twm-managed-window--modal .twm-modal__row.twm-is-invalid select,
|
|
368
|
+
.twm-managed-window--modal .twm-modal__row.twm-is-invalid textarea {
|
|
369
|
+
border-color: #b03a3a;
|
|
370
|
+
}
|
|
371
|
+
/* Hint + per-field error — small dim line that sits *under* the input,
|
|
372
|
+
* spanning only the input column so it aligns with the field above. */
|
|
373
|
+
.twm-managed-window--modal .twm-modal__hint--field {
|
|
374
|
+
grid-column: 2;
|
|
375
|
+
color: var(--ea-text-muted);
|
|
376
|
+
font-size: 10.5px;
|
|
377
|
+
margin-top: 3px;
|
|
378
|
+
line-height: 1.4;
|
|
379
|
+
font-style: italic;
|
|
380
|
+
}
|
|
381
|
+
.twm-managed-window--modal .twm-modal__field-error {
|
|
382
|
+
grid-column: 2;
|
|
383
|
+
color: #f0a8a8;
|
|
384
|
+
font-size: 10.5px;
|
|
385
|
+
margin-top: 3px;
|
|
386
|
+
line-height: 1.4;
|
|
387
|
+
}
|
|
388
|
+
/* Section divider — small uppercase heading with a long em-dash style
|
|
389
|
+
* underline that runs to the right edge of the field column. Reads
|
|
390
|
+
* like the section breaks in a dialog/ncurses form. */
|
|
391
|
+
.twm-managed-window--modal .twm-modal__section {
|
|
392
|
+
grid-column: 1 / -1;
|
|
393
|
+
display: flex;
|
|
394
|
+
align-items: center;
|
|
395
|
+
gap: 8px;
|
|
396
|
+
margin: 14px 0 8px;
|
|
397
|
+
padding: 0;
|
|
398
|
+
font-size: 10.5px;
|
|
399
|
+
font-weight: 700;
|
|
400
|
+
text-transform: uppercase;
|
|
401
|
+
letter-spacing: 0.08em;
|
|
402
|
+
color: var(--ea-accent);
|
|
403
|
+
}
|
|
404
|
+
.twm-managed-window--modal .twm-modal__section::after {
|
|
405
|
+
content: '';
|
|
406
|
+
flex: 1;
|
|
407
|
+
height: 1px;
|
|
408
|
+
background: #3a3a3a;
|
|
409
|
+
}
|
|
410
|
+
/* Inline checkbox row — left cell empty (kept for grid alignment), the
|
|
411
|
+
* checkbox + its own label live in column 2. */
|
|
412
|
+
.twm-managed-window--modal .twm-modal__check {
|
|
413
|
+
display: flex;
|
|
414
|
+
align-items: center;
|
|
415
|
+
gap: 8px;
|
|
416
|
+
color: var(--ea-text);
|
|
417
|
+
}
|
|
418
|
+
.twm-managed-window--modal .twm-modal__check input[type="checkbox"] {
|
|
419
|
+
width: 14px;
|
|
420
|
+
height: 14px;
|
|
421
|
+
margin: 0;
|
|
422
|
+
accent-color: var(--ea-accent);
|
|
423
|
+
}
|
|
424
|
+
/* Combobox = input + datalist + a small "↳ will create X" hint. The
|
|
425
|
+
* hint sits below the input, dim accent color. */
|
|
426
|
+
.twm-managed-window--modal .twm-combobox {
|
|
427
|
+
display: flex;
|
|
428
|
+
flex-direction: column;
|
|
429
|
+
gap: 2px;
|
|
430
|
+
}
|
|
431
|
+
.twm-managed-window--modal .twm-combobox__hint {
|
|
432
|
+
font-size: 10.5px;
|
|
433
|
+
color: var(--ea-accent);
|
|
434
|
+
font-style: italic;
|
|
435
|
+
}
|
|
436
|
+
/* Action strip — true footer band, flush against the modal edges,
|
|
437
|
+
* darker chrome bg, top border + 1px highlight above. Right-aligned
|
|
438
|
+
* buttons, primary stays rightmost. */
|
|
439
|
+
.twm-managed-window--modal .twm-modal__actions {
|
|
440
|
+
margin-top: auto;
|
|
441
|
+
display: flex;
|
|
442
|
+
justify-content: flex-end;
|
|
443
|
+
align-items: center;
|
|
444
|
+
gap: 6px;
|
|
445
|
+
background: var(--ea-bg-chrome);
|
|
446
|
+
border-top: 1px solid #4a4a4a;
|
|
447
|
+
box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.03) inset;
|
|
448
|
+
padding: 6px 14px;
|
|
449
|
+
min-height: 32px;
|
|
450
|
+
}
|
|
451
|
+
.twm-managed-window--modal .twm-modal__actions .twm-btn {
|
|
452
|
+
border-radius: 0;
|
|
453
|
+
min-height: 22px;
|
|
454
|
+
padding: 0 14px;
|
|
455
|
+
font-size: var(--ea-font-body);
|
|
456
|
+
letter-spacing: 0.04em;
|
|
457
|
+
text-transform: uppercase;
|
|
458
|
+
}
|
|
459
|
+
.twm-managed-window--modal .twm-modal__actions .twm-btn--primary {
|
|
460
|
+
background: var(--ea-accent);
|
|
461
|
+
border-color: var(--ea-accent);
|
|
462
|
+
color: #fff;
|
|
463
|
+
font-weight: 600;
|
|
464
|
+
}
|
|
465
|
+
.twm-managed-window--modal .twm-modal__actions .twm-btn--primary:hover:not(:disabled) {
|
|
466
|
+
background: #1683c4;
|
|
467
|
+
border-color: #1683c4;
|
|
468
|
+
}
|
|
469
|
+
.twm-managed-window--modal .twm-modal__actions .twm-btn--danger {
|
|
470
|
+
background: #8b2a2a;
|
|
471
|
+
border-color: #8b2a2a;
|
|
472
|
+
color: #fff;
|
|
473
|
+
}
|
|
474
|
+
.twm-managed-window--modal .twm-modal__actions .twm-btn--danger:hover:not(:disabled) {
|
|
475
|
+
background: #a13434;
|
|
476
|
+
border-color: #a13434;
|
|
477
|
+
}
|
|
478
|
+
/* openConfirm uses .twm-modal__body for the message paragraph. */
|
|
479
|
+
.twm-managed-window--modal .twm-modal__body {
|
|
480
|
+
padding: 0;
|
|
481
|
+
line-height: 1.5;
|
|
482
|
+
color: var(--ea-text);
|
|
483
|
+
flex: 1 1 auto;
|
|
484
|
+
}
|
|
485
|
+
/* Form-level error banner — left accent stripe, dim red panel. */
|
|
486
|
+
.twm-managed-window--modal .twm-modal__error {
|
|
487
|
+
margin-top: 8px;
|
|
488
|
+
padding: 6px 10px;
|
|
489
|
+
background: rgba(176, 58, 58, 0.12);
|
|
490
|
+
color: #f0a8a8;
|
|
491
|
+
border: 1px solid #6a2828;
|
|
492
|
+
border-left: 3px solid #b03a3a;
|
|
493
|
+
font-size: 10.5px;
|
|
494
|
+
line-height: 1.4;
|
|
495
|
+
}
|
|
496
|
+
.twm-modal__form,
|
|
497
|
+
.twm-modal__body {
|
|
498
|
+
padding: var(--ea-pad-loose);
|
|
499
|
+
overflow: auto;
|
|
500
|
+
}
|
|
501
|
+
.twm-modal__row {
|
|
502
|
+
display: grid;
|
|
503
|
+
grid-template-columns: minmax(120px, 32%) 1fr;
|
|
504
|
+
align-items: center;
|
|
505
|
+
gap: var(--ea-pad-loose);
|
|
506
|
+
margin-bottom: var(--ea-pad-snug);
|
|
507
|
+
font-size: var(--ea-font-body);
|
|
508
|
+
color: var(--ea-text);
|
|
509
|
+
}
|
|
510
|
+
.twm-modal__row--multiline {
|
|
511
|
+
align-items: start;
|
|
512
|
+
}
|
|
513
|
+
.twm-modal__row > span {
|
|
514
|
+
color: var(--ea-text-dim);
|
|
515
|
+
font-size: var(--ea-font-body);
|
|
516
|
+
padding-top: 1px;
|
|
517
|
+
}
|
|
518
|
+
.twm-modal__row input[type="text"],
|
|
519
|
+
.twm-modal__row input[type="number"],
|
|
520
|
+
.twm-modal__row input[type="password"],
|
|
521
|
+
.twm-modal__row input[type="search"],
|
|
522
|
+
.twm-modal__row input[type="email"],
|
|
523
|
+
.twm-modal__row select,
|
|
524
|
+
.twm-modal__row textarea {
|
|
525
|
+
background: var(--ea-bg-well);
|
|
526
|
+
color: var(--ea-text);
|
|
527
|
+
border: 1px solid var(--ea-border);
|
|
528
|
+
padding: 0 var(--ea-pad-snug);
|
|
529
|
+
border-radius: 0;
|
|
530
|
+
font: inherit;
|
|
531
|
+
font-size: var(--ea-font-body);
|
|
532
|
+
width: 100%;
|
|
533
|
+
box-sizing: border-box;
|
|
534
|
+
}
|
|
535
|
+
.twm-modal__row textarea {
|
|
536
|
+
padding: var(--ea-pad-snug);
|
|
537
|
+
min-height: calc(var(--ea-row-h) * 3);
|
|
538
|
+
line-height: 1.45;
|
|
539
|
+
resize: vertical;
|
|
540
|
+
font-family: inherit;
|
|
541
|
+
}
|
|
542
|
+
.twm-modal__row input:focus,
|
|
543
|
+
.twm-modal__row select:focus,
|
|
544
|
+
.twm-modal__row textarea:focus {
|
|
545
|
+
outline: none;
|
|
546
|
+
border-color: var(--ea-accent);
|
|
547
|
+
box-shadow: 0 0 0 1px var(--ea-accent);
|
|
548
|
+
}
|
|
549
|
+
.twm-modal__row.twm-is-invalid input,
|
|
550
|
+
.twm-modal__row.twm-is-invalid select,
|
|
551
|
+
.twm-modal__row.twm-is-invalid textarea {
|
|
552
|
+
border-color: #b03a3a;
|
|
553
|
+
}
|
|
554
|
+
/* Per-field help text — small dim line that sits under the row,
|
|
555
|
+
* spanning only the input column so it aligns with the field. */
|
|
556
|
+
.twm-modal__hint--field {
|
|
557
|
+
grid-column: 2;
|
|
558
|
+
color: var(--ea-text-dim);
|
|
559
|
+
font-size: var(--ea-font-small, 11px);
|
|
560
|
+
margin-top: 2px;
|
|
561
|
+
margin-bottom: var(--ea-pad-snug);
|
|
562
|
+
line-height: 1.4;
|
|
563
|
+
}
|
|
564
|
+
.twm-modal__field-error {
|
|
565
|
+
grid-column: 2;
|
|
566
|
+
color: #f0a8a8;
|
|
567
|
+
font-size: var(--ea-font-small, 11px);
|
|
568
|
+
margin-top: 2px;
|
|
569
|
+
margin-bottom: var(--ea-pad-snug);
|
|
570
|
+
line-height: 1.4;
|
|
571
|
+
}
|
|
572
|
+
/* Section divider — small uppercase label with a thin top border, used
|
|
573
|
+
* to group fields inside a longer form. */
|
|
574
|
+
.twm-modal__section {
|
|
575
|
+
grid-column: 1 / -1;
|
|
576
|
+
margin: var(--ea-pad-loose) 0 var(--ea-pad-snug);
|
|
577
|
+
padding-top: var(--ea-pad-snug);
|
|
578
|
+
border-top: 1px solid var(--ea-border);
|
|
579
|
+
font-size: var(--ea-font-small, 11px);
|
|
580
|
+
text-transform: uppercase;
|
|
581
|
+
letter-spacing: 0.06em;
|
|
582
|
+
color: var(--ea-text-muted);
|
|
583
|
+
}
|
|
584
|
+
.twm-modal__section:first-child {
|
|
585
|
+
margin-top: 0;
|
|
586
|
+
padding-top: 0;
|
|
587
|
+
border-top: none;
|
|
588
|
+
}
|
|
589
|
+
/* Inline checkbox — label sits on the right of the box so the row
|
|
590
|
+
* reads naturally. The empty <span> in column 1 keeps grid alignment
|
|
591
|
+
* with text-input rows. */
|
|
592
|
+
.twm-modal__check {
|
|
593
|
+
display: flex;
|
|
594
|
+
align-items: center;
|
|
595
|
+
gap: var(--ea-pad-snug);
|
|
596
|
+
color: var(--ea-text);
|
|
597
|
+
}
|
|
598
|
+
.twm-modal__check input[type="checkbox"] {
|
|
599
|
+
width: 14px;
|
|
600
|
+
height: 14px;
|
|
601
|
+
margin: 0;
|
|
602
|
+
accent-color: var(--ea-accent);
|
|
603
|
+
}
|
|
604
|
+
/* Dependent-property pick-or-create — a type-ahead combobox. The user
|
|
605
|
+
picks an existing option or types a new value (created on submit).
|
|
606
|
+
With no options it's just a text field — the "nothing defined yet"
|
|
607
|
+
case needs no special handling. */
|
|
608
|
+
.twm-combobox {
|
|
609
|
+
display: flex;
|
|
610
|
+
flex-direction: column;
|
|
611
|
+
gap: 3px;
|
|
612
|
+
}
|
|
613
|
+
.twm-combobox input { width: 100%; box-sizing: border-box; }
|
|
614
|
+
.twm-combobox__hint {
|
|
615
|
+
font-size: var(--ea-font-small, 11px);
|
|
616
|
+
color: var(--ea-accent);
|
|
617
|
+
}
|
|
618
|
+
.twm-modal__actions {
|
|
619
|
+
display: flex;
|
|
620
|
+
justify-content: flex-end;
|
|
621
|
+
align-items: center;
|
|
622
|
+
gap: var(--ea-pad-snug);
|
|
623
|
+
background: var(--ea-bg-chrome);
|
|
624
|
+
}
|
|
625
|
+
.twm-modal__actions .twm-btn {
|
|
626
|
+
border-radius: 0;
|
|
627
|
+
min-height: calc(var(--ea-row-h) + 4px);
|
|
628
|
+
padding: 0 var(--ea-pad-loose);
|
|
629
|
+
}
|
|
630
|
+
.twm-modal__error {
|
|
631
|
+
margin: 0 var(--ea-pad-loose) var(--ea-pad-snug);
|
|
632
|
+
padding: var(--ea-pad-snug) var(--ea-pad-loose);
|
|
633
|
+
border-radius: 0;
|
|
634
|
+
background: #3a1f1f;
|
|
635
|
+
color: #f0a8a8;
|
|
636
|
+
border: 1px solid #5a2a2a;
|
|
637
|
+
font-size: var(--ea-font-body);
|
|
638
|
+
}
|
|
639
|
+
.twm-modal__body {
|
|
640
|
+
padding: var(--ea-pad-loose);
|
|
641
|
+
}
|
|
642
|
+
.twm-slide-out-panel__body .twm-autocomplete-field input,
|
|
643
|
+
.twm-slide-out-panel__body .twm-autocomplete-field select,
|
|
644
|
+
.twm-slide-out-panel__body .twm-autocomplete-field button {
|
|
645
|
+
height: var(--ea-row-h);
|
|
646
|
+
min-height: var(--ea-row-h);
|
|
647
|
+
font-size: var(--ea-font-body);
|
|
648
|
+
padding: 0 var(--ea-pad-snug);
|
|
649
|
+
background: var(--ea-bg-well);
|
|
650
|
+
color: var(--ea-text);
|
|
651
|
+
border: 1px solid var(--ea-border);
|
|
652
|
+
border-radius: 2px;
|
|
653
|
+
line-height: 1;
|
|
654
|
+
}
|
|
655
|
+
/* ══════════════════════════════════════════════════════════════════════
|
|
656
|
+
* SFC mode overview — dense two-table layout (Sectors + Flows always
|
|
657
|
+
* visible). Header strip on top with summary stats; two stacked
|
|
658
|
+
* DataTable panes below, each independently scrollable. Both tables
|
|
659
|
+
* are keyboard-navigable; rows are click-to-open.
|
|
660
|
+
*
|
|
661
|
+
* No cards, no pills, no rounded boxes — matches the dense aesthetic
|
|
662
|
+
* of sector / market / agent tabs (.twm-entity-header + .ea-table).
|
|
663
|
+
* ══════════════════════════════════════════════════════════════════════ */
|
|
664
|
+
.twm-landing {
|
|
665
|
+
display: flex;
|
|
666
|
+
flex-direction: column;
|
|
667
|
+
height: 100%;
|
|
668
|
+
min-height: 0;
|
|
669
|
+
overflow: hidden; /* keep the actions strip anchored at the bottom */
|
|
670
|
+
background: var(--ea-bg-chrome);
|
|
671
|
+
color: var(--ea-text);
|
|
672
|
+
font-size: var(--ea-font-body);
|
|
673
|
+
}
|
|
674
|
+
.twm-landing__split {
|
|
675
|
+
flex: 1 1 0; /* shrink-fit so footer always shows */
|
|
676
|
+
min-height: 0;
|
|
677
|
+
display: flex;
|
|
678
|
+
flex-direction: column;
|
|
679
|
+
overflow: hidden;
|
|
680
|
+
}
|
|
681
|
+
.twm-landing__pane {
|
|
682
|
+
flex: 1 1 50%;
|
|
683
|
+
min-height: 0;
|
|
684
|
+
display: flex;
|
|
685
|
+
flex-direction: column;
|
|
686
|
+
border-top: 1px solid var(--ea-border);
|
|
687
|
+
}
|
|
688
|
+
.twm-landing__pane:first-child { border-top: none; }
|
|
689
|
+
/* Single-pane landings (Assets / Markets / Archetypes / Agents /
|
|
690
|
+
* Scenarios / KPIs / …) — the lone pane should fill 100% of the
|
|
691
|
+
* available vertical space inside the split. Without this the
|
|
692
|
+
* `flex: 1 1 50%` default leaves the pane sized to its content. */
|
|
693
|
+
.twm-landing__split--single > .twm-landing__pane {
|
|
694
|
+
flex: 1 1 auto;
|
|
695
|
+
}
|
|
696
|
+
.twm-landing__split--single > .twm-landing__pane
|
|
697
|
+
> .twm-landing__table {
|
|
698
|
+
flex: 1 1 0;
|
|
699
|
+
min-height: 0;
|
|
700
|
+
}
|
|
701
|
+
.twm-landing__pane-head {
|
|
702
|
+
display: flex;
|
|
703
|
+
align-items: center;
|
|
704
|
+
gap: var(--ea-pad-snug);
|
|
705
|
+
padding: 0 var(--ea-pad-snug);
|
|
706
|
+
height: 22px;
|
|
707
|
+
min-height: 22px;
|
|
708
|
+
background: var(--ea-bg-chrome);
|
|
709
|
+
border-bottom: 1px solid var(--ea-border-soft);
|
|
710
|
+
flex-shrink: 0;
|
|
711
|
+
}
|
|
712
|
+
.twm-landing__pane-title {
|
|
713
|
+
font-size: 10.5px;
|
|
714
|
+
text-transform: uppercase;
|
|
715
|
+
letter-spacing: 0.04em;
|
|
716
|
+
font-weight: 600;
|
|
717
|
+
color: var(--ea-text);
|
|
718
|
+
}
|
|
719
|
+
.twm-landing__pane-hint {
|
|
720
|
+
margin-left: auto;
|
|
721
|
+
color: var(--ea-text-dim);
|
|
722
|
+
font-size: 10px;
|
|
723
|
+
}
|
|
724
|
+
.twm-landing__pane--focused .twm-landing__pane-head {
|
|
725
|
+
background: rgba(17, 119, 179, 0.10);
|
|
726
|
+
}
|
|
727
|
+
.twm-landing__pane--focused .twm-landing__pane-title {
|
|
728
|
+
color: var(--ea-accent);
|
|
729
|
+
}
|
|
730
|
+
.twm-landing__table {
|
|
731
|
+
flex: 1;
|
|
732
|
+
min-height: 0;
|
|
733
|
+
overflow: auto;
|
|
734
|
+
background: var(--ea-bg-content);
|
|
735
|
+
}
|
|
736
|
+
/* Kind-filter dropdown in the Archetypes landing header. Same dark
|
|
737
|
+
* styling as the rest of the inputs — the bare browser default reads
|
|
738
|
+
* as light grey. */
|
|
739
|
+
.twm-entity-header__field select {
|
|
740
|
+
background-color: var(--ea-input-bg, #1e1e1e);
|
|
741
|
+
color: var(--ea-text, #ddd);
|
|
742
|
+
border: 1px solid var(--ea-border, #444);
|
|
743
|
+
padding: 0.2em 1.6em 0.2em 0.5em;
|
|
744
|
+
font: inherit;
|
|
745
|
+
font-size: 0.85em;
|
|
746
|
+
-webkit-appearance: none;
|
|
747
|
+
-moz-appearance: none;
|
|
748
|
+
appearance: none;
|
|
749
|
+
background-image: linear-gradient(45deg, transparent 50%, var(--ea-text-muted, #888) 50%),
|
|
750
|
+
linear-gradient(135deg, var(--ea-text-muted, #888) 50%, transparent 50%);
|
|
751
|
+
background-position: calc(100% - 12px) 50%, calc(100% - 8px) 50%;
|
|
752
|
+
background-size: 4px 4px, 4px 4px;
|
|
753
|
+
background-repeat: no-repeat;
|
|
754
|
+
cursor: pointer;
|
|
755
|
+
}
|
|
756
|
+
.twm-entity-header__field select:focus {
|
|
757
|
+
outline: none;
|
|
758
|
+
border-color: var(--ea-accent, #5aa3e8);
|
|
759
|
+
}
|
|
760
|
+
.twm-entity-header__field select option { background: var(--ea-input-bg, #1e1e1e); color: var(--ea-text, #ddd); }
|
|
761
|
+
.twm-landing__table table {
|
|
762
|
+
width: 100%;
|
|
763
|
+
}
|
|
764
|
+
.twm-landing__table thead th {
|
|
765
|
+
position: sticky;
|
|
766
|
+
top: 0;
|
|
767
|
+
background: var(--ea-bg-chrome);
|
|
768
|
+
z-index: 1;
|
|
769
|
+
}
|
|
770
|
+
.twm-landing__table tbody tr:hover {
|
|
771
|
+
background: rgba(255, 255, 255, 0.04);
|
|
772
|
+
}
|
|
773
|
+
/* Keyboard cursor highlight — shared between every overview's DataTable
|
|
774
|
+
so the same arrow-key affordance reads identically across modes. */
|
|
775
|
+
.twm-row-cursor {
|
|
776
|
+
box-shadow: inset 2px 0 0 var(--ea-accent);
|
|
777
|
+
background: rgba(17, 119, 179, 0.12) !important;
|
|
778
|
+
}
|
|
779
|
+
/* Quick-action strip — horizontal bar below the tables. Same chrome
|
|
780
|
+
tone as `.twm-entity-header` so the page reads as a single stack:
|
|
781
|
+
header / two tables / actions. Buttons are flat (no border-radius
|
|
782
|
+
beyond 2px), icon + short label, keyboard-shortcut hint on the right.
|
|
783
|
+
Reused across every mode overview that wants a quick-action row. */
|
|
784
|
+
.twm-landing__actions {
|
|
785
|
+
display: flex;
|
|
786
|
+
align-items: center;
|
|
787
|
+
gap: var(--ea-pad-snug);
|
|
788
|
+
height: 30px;
|
|
789
|
+
min-height: 30px;
|
|
790
|
+
padding: 0 var(--ea-pad-snug);
|
|
791
|
+
background: var(--ea-bg-well);
|
|
792
|
+
border-top: 1px solid var(--ea-border);
|
|
793
|
+
flex: 0 0 30px;
|
|
794
|
+
position: relative;
|
|
795
|
+
z-index: 2;
|
|
796
|
+
}
|
|
797
|
+
.twm-landing__actions-spacer { flex: 1; }
|
|
798
|
+
.twm-landing__actions-hint {
|
|
799
|
+
color: var(--ea-text-dim);
|
|
800
|
+
font-size: 10px;
|
|
801
|
+
}
|
|
802
|
+
.twm-action-btn {
|
|
803
|
+
display: inline-flex;
|
|
804
|
+
align-items: center;
|
|
805
|
+
gap: 4px;
|
|
806
|
+
height: 20px;
|
|
807
|
+
padding: 0 var(--ea-pad-snug);
|
|
808
|
+
background: transparent;
|
|
809
|
+
border: 1px solid var(--ea-border-soft);
|
|
810
|
+
border-radius: 2px;
|
|
811
|
+
color: var(--ea-text);
|
|
812
|
+
font-size: var(--ea-font-body);
|
|
813
|
+
cursor: pointer;
|
|
814
|
+
}
|
|
815
|
+
.twm-action-btn:hover {
|
|
816
|
+
border-color: var(--ea-accent);
|
|
817
|
+
background: rgba(17, 119, 179, 0.08);
|
|
818
|
+
}
|
|
819
|
+
.twm-action-btn:disabled,
|
|
820
|
+
.twm-action-btn[aria-disabled="true"] {
|
|
821
|
+
color: var(--ea-text-dim);
|
|
822
|
+
cursor: not-allowed;
|
|
823
|
+
}
|
|
824
|
+
/* Inline keyboard-shortcut chip rendered inside an .twm-action-btn —
|
|
825
|
+
* the visible "[N]" that tells the user which letter fires the action.
|
|
826
|
+
* mountLandingActions appends it after the label and `landing_table.js`
|
|
827
|
+
* keeps the keydown handler in sync, so the chip and the hotkey can
|
|
828
|
+
* never drift apart. */
|
|
829
|
+
.twm-action-btn__kbd {
|
|
830
|
+
margin-left: 4px;
|
|
831
|
+
padding: 0 4px;
|
|
832
|
+
font-family: var(--ea-mono, monospace);
|
|
833
|
+
font-size: 10px;
|
|
834
|
+
line-height: 14px;
|
|
835
|
+
color: var(--ea-text-dim);
|
|
836
|
+
border: 1px solid var(--ea-border-soft);
|
|
837
|
+
border-radius: 2px;
|
|
838
|
+
background: var(--ea-bg-strip, transparent);
|
|
839
|
+
}
|
|
840
|
+
.twm-action-btn:hover .twm-action-btn__kbd {
|
|
841
|
+
color: var(--ea-text);
|
|
842
|
+
border-color: var(--ea-accent);
|
|
843
|
+
}
|
|
844
|
+
/* ── Starter-gallery picker (G1.3) ─────────────────────────────────
|
|
845
|
+
* Card grid used by every "New X" modal to choose a starter shape.
|
|
846
|
+
* Replaces the legacy template <select> dropdowns. Cards stack
|
|
847
|
+
* naturally into rows of 2–3 depending on modal width; selecting a
|
|
848
|
+
* card simply toggles `.is-selected`. */
|
|
849
|
+
.twm-gallery-picker {
|
|
850
|
+
display: grid;
|
|
851
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
852
|
+
gap: 8px;
|
|
853
|
+
align-content: start;
|
|
854
|
+
max-height: 320px;
|
|
855
|
+
overflow-y: auto;
|
|
856
|
+
padding: 4px;
|
|
857
|
+
border: 1px solid var(--ea-border, #2a2a2a);
|
|
858
|
+
background: var(--ea-input-bg, #1e1e1e);
|
|
859
|
+
}
|
|
860
|
+
.twm-gallery-picker__empty {
|
|
861
|
+
grid-column: 1 / -1;
|
|
862
|
+
padding: 16px;
|
|
863
|
+
color: var(--ea-text-dim, #777);
|
|
864
|
+
font-style: italic;
|
|
865
|
+
text-align: center;
|
|
866
|
+
}
|
|
867
|
+
.twm-gallery-card {
|
|
868
|
+
display: flex;
|
|
869
|
+
flex-direction: column;
|
|
870
|
+
gap: 4px;
|
|
871
|
+
align-items: flex-start;
|
|
872
|
+
text-align: left;
|
|
873
|
+
padding: 10px 12px;
|
|
874
|
+
background: var(--ea-bg-content, #181818);
|
|
875
|
+
border: 1px solid var(--ea-border, #2a2a2a);
|
|
876
|
+
color: var(--ea-text, #ddd);
|
|
877
|
+
cursor: pointer;
|
|
878
|
+
font-family: inherit;
|
|
879
|
+
font-size: 13px;
|
|
880
|
+
transition: background-color 0.08s ease,
|
|
881
|
+
border-color 0.08s ease;
|
|
882
|
+
}
|
|
883
|
+
.twm-gallery-card:hover {
|
|
884
|
+
background: rgba(255, 255, 255, 0.04);
|
|
885
|
+
border-color: var(--ea-border-soft, #3a3a3a);
|
|
886
|
+
}
|
|
887
|
+
.twm-gallery-card__label {
|
|
888
|
+
font-weight: 600;
|
|
889
|
+
line-height: 1.2;
|
|
890
|
+
}
|
|
891
|
+
.twm-gallery-card__desc {
|
|
892
|
+
margin: 0;
|
|
893
|
+
color: var(--ea-text-muted, #999);
|
|
894
|
+
font-size: 11.5px;
|
|
895
|
+
line-height: 1.35;
|
|
896
|
+
}
|
|
897
|
+
/* Resource-kind picker — verdict-driven dropdown row tinting + the
|
|
898
|
+
* Requirements panel below the picker. Same colour family as the
|
|
899
|
+
* signature panel (match=green, override/partial=amber, missing=red)
|
|
900
|
+
* so the two views read consistently. */
|
|
901
|
+
.twm-autocomplete-field__item[data-type="ok"] { /* default */ }
|
|
902
|
+
.twm-autocomplete-field__item[data-type="partial"] {
|
|
903
|
+
background: rgba(204, 120, 65, 0.08);
|
|
904
|
+
}
|
|
905
|
+
.twm-autocomplete-field__item[data-type="missing"] {
|
|
906
|
+
background: rgba(220, 80, 80, 0.10);
|
|
907
|
+
color: var(--ea-text-dim, rgba(255, 255, 255, 0.55));
|
|
908
|
+
}
|
|
909
|
+
.twm-autocomplete-field__item[data-type="ok"] .twm-autocomplete-field__badge { color: #69f0ae; }
|
|
910
|
+
.twm-autocomplete-field__item[data-type="partial"] .twm-autocomplete-field__badge { color: #ffd180; }
|
|
911
|
+
.twm-autocomplete-field__item[data-type="missing"] .twm-autocomplete-field__badge { color: #f48fb1; }
|
|
912
|
+
|
|
913
|
+
/* ── from tiling/tile.css ───────────────────────── */
|
|
914
|
+
/* tile.css — WM containers, leaves, chrome, splitters. */
|
|
915
|
+
|
|
916
|
+
.twm-shell__main {
|
|
917
|
+
overflow: hidden;
|
|
918
|
+
position: relative;
|
|
919
|
+
}
|
|
920
|
+
.twm-root {
|
|
921
|
+
display: flex;
|
|
922
|
+
height: 100%;
|
|
923
|
+
width: 100%;
|
|
924
|
+
background: var(--surface-app);
|
|
925
|
+
}
|
|
926
|
+
.twm-empty {
|
|
927
|
+
display: flex;
|
|
928
|
+
height: 100%;
|
|
929
|
+
width: 100%;
|
|
930
|
+
align-items: center;
|
|
931
|
+
justify-content: center;
|
|
932
|
+
color: var(--text-muted);
|
|
933
|
+
font-size: var(--font-size-xs);
|
|
934
|
+
}
|
|
935
|
+
/* Split + slot — flex containers along one axis. */
|
|
936
|
+
.twm-split {
|
|
937
|
+
display: flex;
|
|
938
|
+
flex: 1 1 auto;
|
|
939
|
+
overflow: hidden;
|
|
940
|
+
min-width: 0;
|
|
941
|
+
min-height: 0;
|
|
942
|
+
}
|
|
943
|
+
.twm-split--h { flex-direction: row; }
|
|
944
|
+
.twm-split--v { flex-direction: column; }
|
|
945
|
+
.twm-slot {
|
|
946
|
+
display: flex;
|
|
947
|
+
overflow: hidden;
|
|
948
|
+
min-width: 0;
|
|
949
|
+
min-height: 0;
|
|
950
|
+
}
|
|
951
|
+
.twm-slot > .twm-leaf,
|
|
952
|
+
.twm-slot > .twm-split { flex: 1 1 auto; min-width: 0; min-height: 0; }
|
|
953
|
+
/* Splitter handle between siblings. */
|
|
954
|
+
.twm-splitter {
|
|
955
|
+
flex: 0 0 4px;
|
|
956
|
+
background: transparent;
|
|
957
|
+
position: relative;
|
|
958
|
+
z-index: 1;
|
|
959
|
+
}
|
|
960
|
+
.twm-splitter:hover,
|
|
961
|
+
body.twm-dragging .twm-splitter { background: var(--accent-bright); opacity: 0.5; }
|
|
962
|
+
.twm-splitter--h { cursor: ew-resize; }
|
|
963
|
+
.twm-splitter--v { cursor: ns-resize; height: 4px; flex: 0 0 4px; }
|
|
964
|
+
body.twm-dragging * { user-select: none; }
|
|
965
|
+
/* Leaf — chrome + body. */
|
|
966
|
+
.twm-leaf {
|
|
967
|
+
display: flex;
|
|
968
|
+
flex-direction: column;
|
|
969
|
+
flex: 1 1 auto;
|
|
970
|
+
background: var(--surface-0);
|
|
971
|
+
border: 1px solid var(--border-subtle);
|
|
972
|
+
border-radius: 0;
|
|
973
|
+
margin: 0;
|
|
974
|
+
overflow: hidden;
|
|
975
|
+
min-width: 0;
|
|
976
|
+
min-height: 0;
|
|
977
|
+
outline: none;
|
|
978
|
+
}
|
|
979
|
+
.twm-leaf--focused {
|
|
980
|
+
border-color: var(--accent-bright);
|
|
981
|
+
box-shadow: 0 0 0 1px rgba(0, 122, 204, 0.25) inset;
|
|
982
|
+
}
|
|
983
|
+
.twm-leaf__chrome {
|
|
984
|
+
display: flex;
|
|
985
|
+
align-items: center;
|
|
986
|
+
justify-content: space-between;
|
|
987
|
+
gap: var(--space-md);
|
|
988
|
+
padding: 0 var(--space-sm);
|
|
989
|
+
height: 22px;
|
|
990
|
+
/* Pin the chrome like its sibling .twm-leaf__tabbar. Without
|
|
991
|
+
* flex-shrink:0 the chrome inherits the default `0 1 auto` and is the
|
|
992
|
+
* only fixed-height bar the flex column may shrink: a tab whose content
|
|
993
|
+
* has a tall max-content height (e.g. Watch — rows min-height:60px +
|
|
994
|
+
* Monaco editor + hits + help aside) inflates .twm-leaf__body's
|
|
995
|
+
* flex-basis:auto, and the negative free space gets distributed onto
|
|
996
|
+
* the chrome too, visibly changing its height. The body (overflow:auto)
|
|
997
|
+
* is what should absorb the overflow, never the chrome. */
|
|
998
|
+
flex: 0 0 auto;
|
|
999
|
+
background: var(--surface-1);
|
|
1000
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1001
|
+
user-select: none;
|
|
1002
|
+
font-size: var(--font-size-xs);
|
|
1003
|
+
}
|
|
1004
|
+
.twm-leaf--focused .twm-leaf__chrome {
|
|
1005
|
+
background: var(--surface-2);
|
|
1006
|
+
color: var(--text-primary);
|
|
1007
|
+
}
|
|
1008
|
+
.twm-leaf__title {
|
|
1009
|
+
color: var(--text-secondary);
|
|
1010
|
+
white-space: nowrap;
|
|
1011
|
+
text-overflow: ellipsis;
|
|
1012
|
+
overflow: hidden;
|
|
1013
|
+
text-transform: lowercase;
|
|
1014
|
+
letter-spacing: 0.02em;
|
|
1015
|
+
}
|
|
1016
|
+
.twm-leaf__actions { display: inline-flex; gap: 2px; }
|
|
1017
|
+
.twm-leaf__btn {
|
|
1018
|
+
display: inline-flex;
|
|
1019
|
+
align-items: center;
|
|
1020
|
+
justify-content: center;
|
|
1021
|
+
width: 18px;
|
|
1022
|
+
height: 18px;
|
|
1023
|
+
padding: 0;
|
|
1024
|
+
background: transparent;
|
|
1025
|
+
border: none;
|
|
1026
|
+
color: var(--text-muted);
|
|
1027
|
+
cursor: pointer;
|
|
1028
|
+
border-radius: 0;
|
|
1029
|
+
}
|
|
1030
|
+
.twm-leaf__btn:hover { background: var(--state-hover); color: var(--text-primary); }
|
|
1031
|
+
.twm-leaf__body {
|
|
1032
|
+
flex: 1 1 auto;
|
|
1033
|
+
overflow: auto;
|
|
1034
|
+
min-height: 0;
|
|
1035
|
+
background: var(--surface-0);
|
|
1036
|
+
}
|
|
1037
|
+
/* Bottom tab strip — appears only when the leaf carries >1 tab.
|
|
1038
|
+
* Trapezoidal/spreadsheet-style tabs (slanted top corners) sit on a
|
|
1039
|
+
* thin bar at the bottom of the tile, sharing the visual language of
|
|
1040
|
+
* the bottom panel's chrome (surface-1, square corners, 11 px label).
|
|
1041
|
+
*
|
|
1042
|
+
* The hamburger button on the left opens the per-tile "browse this
|
|
1043
|
+
* page's content" menu (`wm.ctx.onTileTabMenu`). */
|
|
1044
|
+
.twm-leaf__tabbar {
|
|
1045
|
+
flex: 0 0 auto;
|
|
1046
|
+
display: flex;
|
|
1047
|
+
align-items: stretch;
|
|
1048
|
+
height: 22px;
|
|
1049
|
+
background: var(--surface-1);
|
|
1050
|
+
border-top: 1px solid var(--border-default);
|
|
1051
|
+
padding: 0 4px;
|
|
1052
|
+
overflow: hidden;
|
|
1053
|
+
user-select: none;
|
|
1054
|
+
font-size: var(--font-size-xs);
|
|
1055
|
+
}
|
|
1056
|
+
.twm-leaf__tabbar--hidden { display: none; }
|
|
1057
|
+
.twm-leaf__tab-hamburger {
|
|
1058
|
+
flex: 0 0 auto;
|
|
1059
|
+
display: inline-flex;
|
|
1060
|
+
align-items: center;
|
|
1061
|
+
justify-content: center;
|
|
1062
|
+
width: 22px;
|
|
1063
|
+
height: 22px;
|
|
1064
|
+
margin-right: 4px;
|
|
1065
|
+
padding: 0;
|
|
1066
|
+
background: transparent;
|
|
1067
|
+
border: none;
|
|
1068
|
+
color: var(--text-muted);
|
|
1069
|
+
cursor: pointer;
|
|
1070
|
+
border-radius: 0;
|
|
1071
|
+
}
|
|
1072
|
+
.twm-leaf__tab-hamburger:hover {
|
|
1073
|
+
color: var(--text-primary);
|
|
1074
|
+
background: var(--state-hover);
|
|
1075
|
+
}
|
|
1076
|
+
.twm-leaf__tabs {
|
|
1077
|
+
list-style: none;
|
|
1078
|
+
padding: 0;
|
|
1079
|
+
margin: 0;
|
|
1080
|
+
display: flex;
|
|
1081
|
+
align-items: stretch;
|
|
1082
|
+
flex: 1 1 auto;
|
|
1083
|
+
min-width: 0;
|
|
1084
|
+
overflow-x: auto;
|
|
1085
|
+
scrollbar-width: none;
|
|
1086
|
+
}
|
|
1087
|
+
.twm-leaf__tabs::-webkit-scrollbar { display: none; }
|
|
1088
|
+
.twm-leaf__tab {
|
|
1089
|
+
/* Spreadsheet-style PARALLELOGRAM: both left and right edges
|
|
1090
|
+
* slanted by ~8 px in the same direction (top-right / bottom-left
|
|
1091
|
+
* pulled inward), giving each tab a uniform slant. Reads as the
|
|
1092
|
+
* old Excel/Lotus sheet-tab look without the trapezoid's bottom
|
|
1093
|
+
* flare. */
|
|
1094
|
+
position: relative;
|
|
1095
|
+
display: inline-flex;
|
|
1096
|
+
align-items: center;
|
|
1097
|
+
gap: 4px;
|
|
1098
|
+
padding: 2px 14px 2px 14px;
|
|
1099
|
+
/* Tabs sit flush against each other — no spacing between them.
|
|
1100
|
+
* The parallelogram clip-path provides the visual separation on
|
|
1101
|
+
* its own (each tab's left edge starts at 8 px in, the previous
|
|
1102
|
+
* tab's right edge ends 8 px in; they interlock without overlap). */
|
|
1103
|
+
margin-right: 0;
|
|
1104
|
+
background: var(--surface-2);
|
|
1105
|
+
color: var(--text-muted);
|
|
1106
|
+
border: none;
|
|
1107
|
+
cursor: pointer;
|
|
1108
|
+
line-height: 1;
|
|
1109
|
+
white-space: nowrap;
|
|
1110
|
+
clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
|
|
1111
|
+
}
|
|
1112
|
+
.twm-leaf__tab:hover {
|
|
1113
|
+
background: var(--surface-3);
|
|
1114
|
+
color: var(--text-primary);
|
|
1115
|
+
}
|
|
1116
|
+
.twm-leaf__tab--on {
|
|
1117
|
+
background: var(--surface-0); /* matches the body — looks lifted */
|
|
1118
|
+
color: var(--text-bright);
|
|
1119
|
+
/* Active tab gets a subtle border on the slanted edge so it
|
|
1120
|
+
* separates from the surface-1 bar. Done via a layered ::before
|
|
1121
|
+
* because clip-path drops the actual border. */
|
|
1122
|
+
}
|
|
1123
|
+
.twm-leaf__tab--on::before {
|
|
1124
|
+
content: '';
|
|
1125
|
+
position: absolute;
|
|
1126
|
+
inset: 0;
|
|
1127
|
+
pointer-events: none;
|
|
1128
|
+
border-top: 1px solid var(--accent-bright, #1683c4);
|
|
1129
|
+
clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
|
|
1130
|
+
}
|
|
1131
|
+
.twm-leaf__tab-label {
|
|
1132
|
+
overflow: hidden;
|
|
1133
|
+
text-overflow: ellipsis;
|
|
1134
|
+
max-width: 160px;
|
|
1135
|
+
text-transform: lowercase;
|
|
1136
|
+
letter-spacing: 0.02em;
|
|
1137
|
+
}
|
|
1138
|
+
.twm-leaf__tab-close {
|
|
1139
|
+
display: inline-flex;
|
|
1140
|
+
align-items: center;
|
|
1141
|
+
justify-content: center;
|
|
1142
|
+
width: 14px;
|
|
1143
|
+
height: 14px;
|
|
1144
|
+
margin-left: 2px;
|
|
1145
|
+
padding: 0;
|
|
1146
|
+
background: transparent;
|
|
1147
|
+
border: none;
|
|
1148
|
+
color: inherit;
|
|
1149
|
+
opacity: 0.6;
|
|
1150
|
+
cursor: pointer;
|
|
1151
|
+
border-radius: 0;
|
|
1152
|
+
}
|
|
1153
|
+
.twm-leaf__tab-close:hover {
|
|
1154
|
+
opacity: 1;
|
|
1155
|
+
color: var(--text-bright);
|
|
1156
|
+
}
|
|
1157
|
+
/* ── Tile tab menu (hamburger pop-out) ────────────────────────────
|
|
1158
|
+
* Anchored above the hamburger button. Lists every entity that the
|
|
1159
|
+
* tile's home page would show, grouped by source, with search +
|
|
1160
|
+
* pagination. Click a row → opens in a new tab on the originating
|
|
1161
|
+
* tile (see install._tileTabMenu). */
|
|
1162
|
+
.twm-tile-tabmenu-overlay {
|
|
1163
|
+
position: fixed;
|
|
1164
|
+
inset: 0;
|
|
1165
|
+
z-index: var(--z-modal);
|
|
1166
|
+
pointer-events: none;
|
|
1167
|
+
}
|
|
1168
|
+
.twm-tile-tabmenu {
|
|
1169
|
+
pointer-events: auto;
|
|
1170
|
+
width: 360px;
|
|
1171
|
+
max-height: 480px;
|
|
1172
|
+
display: flex;
|
|
1173
|
+
flex-direction: column;
|
|
1174
|
+
background: var(--surface-1);
|
|
1175
|
+
border: 1px solid var(--border-default);
|
|
1176
|
+
box-shadow: var(--shadow-lg);
|
|
1177
|
+
}
|
|
1178
|
+
.twm-tile-tabmenu__head {
|
|
1179
|
+
display: flex;
|
|
1180
|
+
align-items: center;
|
|
1181
|
+
gap: var(--space-sm);
|
|
1182
|
+
padding: var(--space-sm) var(--space-md);
|
|
1183
|
+
background: var(--surface-2);
|
|
1184
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1185
|
+
font-size: var(--font-size-xs);
|
|
1186
|
+
text-transform: uppercase;
|
|
1187
|
+
letter-spacing: 0.04em;
|
|
1188
|
+
color: var(--text-secondary);
|
|
1189
|
+
}
|
|
1190
|
+
.twm-tile-tabmenu__head-icon { font-size: 14px; }
|
|
1191
|
+
.twm-tile-tabmenu__head-label { flex: 1 1 auto; }
|
|
1192
|
+
.twm-tile-tabmenu__close {
|
|
1193
|
+
background: transparent;
|
|
1194
|
+
border: none;
|
|
1195
|
+
color: var(--text-muted);
|
|
1196
|
+
cursor: pointer;
|
|
1197
|
+
padding: 0 4px;
|
|
1198
|
+
font-size: 14px;
|
|
1199
|
+
}
|
|
1200
|
+
.twm-tile-tabmenu__close:hover { color: var(--text-primary); }
|
|
1201
|
+
.twm-tile-tabmenu__search {
|
|
1202
|
+
display: flex;
|
|
1203
|
+
align-items: center;
|
|
1204
|
+
gap: var(--space-xs);
|
|
1205
|
+
padding: 4px 8px;
|
|
1206
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1207
|
+
background: var(--surface-1);
|
|
1208
|
+
}
|
|
1209
|
+
.twm-tile-tabmenu__search input {
|
|
1210
|
+
flex: 1 1 auto;
|
|
1211
|
+
background: transparent;
|
|
1212
|
+
border: none;
|
|
1213
|
+
outline: none;
|
|
1214
|
+
color: var(--text-primary);
|
|
1215
|
+
font-size: var(--font-size-sm);
|
|
1216
|
+
}
|
|
1217
|
+
.twm-tile-tabmenu__body {
|
|
1218
|
+
flex: 1 1 auto;
|
|
1219
|
+
overflow-y: auto;
|
|
1220
|
+
padding: 4px 0;
|
|
1221
|
+
}
|
|
1222
|
+
.twm-tile-tabmenu__loading {
|
|
1223
|
+
padding: var(--space-md);
|
|
1224
|
+
color: var(--text-muted);
|
|
1225
|
+
font-size: var(--font-size-xs);
|
|
1226
|
+
text-align: center;
|
|
1227
|
+
}
|
|
1228
|
+
.twm-tile-tabmenu__section {
|
|
1229
|
+
padding: 0 0 4px;
|
|
1230
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1231
|
+
}
|
|
1232
|
+
.twm-tile-tabmenu__section:last-child { border-bottom: none; }
|
|
1233
|
+
.twm-tile-tabmenu__section-head {
|
|
1234
|
+
display: flex;
|
|
1235
|
+
align-items: center;
|
|
1236
|
+
gap: var(--space-xs);
|
|
1237
|
+
padding: 4px 8px;
|
|
1238
|
+
color: var(--text-muted);
|
|
1239
|
+
font-size: 10px;
|
|
1240
|
+
text-transform: uppercase;
|
|
1241
|
+
letter-spacing: 0.05em;
|
|
1242
|
+
}
|
|
1243
|
+
.twm-tile-tabmenu__section-label { flex: 1 1 auto; }
|
|
1244
|
+
.twm-tile-tabmenu__section-count {
|
|
1245
|
+
color: var(--text-muted);
|
|
1246
|
+
background: var(--surface-2);
|
|
1247
|
+
padding: 0 4px;
|
|
1248
|
+
font-size: 9px;
|
|
1249
|
+
}
|
|
1250
|
+
.twm-tile-tabmenu__list {
|
|
1251
|
+
list-style: none;
|
|
1252
|
+
margin: 0;
|
|
1253
|
+
padding: 0;
|
|
1254
|
+
}
|
|
1255
|
+
.twm-tile-tabmenu__item {
|
|
1256
|
+
display: grid;
|
|
1257
|
+
grid-template-columns: 1fr auto;
|
|
1258
|
+
align-items: center;
|
|
1259
|
+
gap: var(--space-sm);
|
|
1260
|
+
padding: 3px 12px;
|
|
1261
|
+
cursor: pointer;
|
|
1262
|
+
color: var(--text-secondary);
|
|
1263
|
+
font-size: var(--font-size-sm);
|
|
1264
|
+
}
|
|
1265
|
+
.twm-tile-tabmenu__item:hover,
|
|
1266
|
+
.twm-tile-tabmenu__item--cursor {
|
|
1267
|
+
background: var(--state-selected);
|
|
1268
|
+
color: var(--text-bright);
|
|
1269
|
+
}
|
|
1270
|
+
.twm-tile-tabmenu__item--cursor {
|
|
1271
|
+
/* Keyboard cursor reads slightly heavier than hover so a
|
|
1272
|
+
* mouse-cursor-on-a-different-row state is still legible. */
|
|
1273
|
+
outline: 1px solid var(--accent-bright, #1683c4);
|
|
1274
|
+
outline-offset: -1px;
|
|
1275
|
+
}
|
|
1276
|
+
.twm-tile-tabmenu__item-hint {
|
|
1277
|
+
color: var(--text-muted);
|
|
1278
|
+
font-size: var(--font-size-xs);
|
|
1279
|
+
}
|
|
1280
|
+
.twm-tile-tabmenu__empty {
|
|
1281
|
+
padding: 6px 12px;
|
|
1282
|
+
color: var(--text-muted);
|
|
1283
|
+
font-size: var(--font-size-xs);
|
|
1284
|
+
}
|
|
1285
|
+
.twm-tile-tabmenu__pager {
|
|
1286
|
+
display: flex;
|
|
1287
|
+
align-items: center;
|
|
1288
|
+
justify-content: center;
|
|
1289
|
+
gap: var(--space-md);
|
|
1290
|
+
padding: 4px 8px;
|
|
1291
|
+
color: var(--text-muted);
|
|
1292
|
+
font-size: var(--font-size-xs);
|
|
1293
|
+
}
|
|
1294
|
+
.twm-tile-tabmenu__pager button {
|
|
1295
|
+
background: transparent;
|
|
1296
|
+
border: 1px solid var(--border-subtle);
|
|
1297
|
+
color: inherit;
|
|
1298
|
+
padding: 1px 6px;
|
|
1299
|
+
cursor: pointer;
|
|
1300
|
+
border-radius: 0;
|
|
1301
|
+
}
|
|
1302
|
+
.twm-tile-tabmenu__pager button:disabled {
|
|
1303
|
+
opacity: 0.4;
|
|
1304
|
+
cursor: not-allowed;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/* ── from tiling/command_palette.css ───────────────────────── */
|
|
1308
|
+
/* command_palette.css — Ctrl+K popup. */
|
|
1309
|
+
|
|
1310
|
+
.twm-cmdpal-overlay {
|
|
1311
|
+
position: fixed;
|
|
1312
|
+
inset: 0;
|
|
1313
|
+
z-index: var(--z-modal);
|
|
1314
|
+
background: rgba(0, 0, 0, 0.55);
|
|
1315
|
+
display: flex;
|
|
1316
|
+
align-items: flex-start;
|
|
1317
|
+
justify-content: center;
|
|
1318
|
+
padding-top: 12vh;
|
|
1319
|
+
}
|
|
1320
|
+
.twm-cmdpal {
|
|
1321
|
+
width: min(640px, 90vw);
|
|
1322
|
+
background: var(--surface-1);
|
|
1323
|
+
border: 1px solid var(--border-default);
|
|
1324
|
+
border-radius: 0;
|
|
1325
|
+
box-shadow: var(--shadow-lg);
|
|
1326
|
+
overflow: hidden;
|
|
1327
|
+
display: flex;
|
|
1328
|
+
flex-direction: column;
|
|
1329
|
+
}
|
|
1330
|
+
/* Input row hosts the optional type-filter chip + the actual text
|
|
1331
|
+
* input. The chip sits to the left of the caret; typing `<prefix>:`
|
|
1332
|
+
* promotes the prefix into the chip and strips it from the input
|
|
1333
|
+
* (see command_palette.js `tryConsumePrefix`). */
|
|
1334
|
+
.twm-cmdpal__inputrow {
|
|
1335
|
+
display: flex;
|
|
1336
|
+
align-items: center;
|
|
1337
|
+
gap: var(--space-sm);
|
|
1338
|
+
padding: var(--space-md) var(--space-lg);
|
|
1339
|
+
background: var(--surface-1);
|
|
1340
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1341
|
+
}
|
|
1342
|
+
.twm-cmdpal__type-chip-slot:empty {
|
|
1343
|
+
/* Empty slot collapses entirely so the input flushes against the
|
|
1344
|
+
* row's left padding when no filter is set. */
|
|
1345
|
+
display: none;
|
|
1346
|
+
}
|
|
1347
|
+
.twm-cmdpal__type-chip {
|
|
1348
|
+
display: inline-flex;
|
|
1349
|
+
align-items: center;
|
|
1350
|
+
gap: var(--space-xs);
|
|
1351
|
+
padding: 2px 4px 2px 8px;
|
|
1352
|
+
background: var(--state-selected, rgba(17, 119, 179, 0.18));
|
|
1353
|
+
border: 1px solid var(--accent-bright, #1683c4);
|
|
1354
|
+
border-radius: 2px;
|
|
1355
|
+
color: var(--text-bright, #fff);
|
|
1356
|
+
font-size: var(--font-size-xs);
|
|
1357
|
+
white-space: nowrap;
|
|
1358
|
+
user-select: none;
|
|
1359
|
+
}
|
|
1360
|
+
.twm-cmdpal__type-chip-icon {
|
|
1361
|
+
font-size: 14px;
|
|
1362
|
+
}
|
|
1363
|
+
.twm-cmdpal__type-chip-label {
|
|
1364
|
+
text-transform: lowercase;
|
|
1365
|
+
letter-spacing: 0.02em;
|
|
1366
|
+
}
|
|
1367
|
+
.twm-cmdpal__type-chip-x {
|
|
1368
|
+
display: inline-flex;
|
|
1369
|
+
align-items: center;
|
|
1370
|
+
justify-content: center;
|
|
1371
|
+
width: 16px;
|
|
1372
|
+
height: 16px;
|
|
1373
|
+
margin-left: 2px;
|
|
1374
|
+
background: transparent;
|
|
1375
|
+
border: none;
|
|
1376
|
+
color: inherit;
|
|
1377
|
+
font-size: 14px;
|
|
1378
|
+
line-height: 1;
|
|
1379
|
+
cursor: pointer;
|
|
1380
|
+
border-radius: 0;
|
|
1381
|
+
}
|
|
1382
|
+
.twm-cmdpal__type-chip-x:hover {
|
|
1383
|
+
background: rgba(255, 255, 255, 0.12);
|
|
1384
|
+
}
|
|
1385
|
+
.twm-cmdpal__input {
|
|
1386
|
+
flex: 1 1 auto;
|
|
1387
|
+
min-width: 0;
|
|
1388
|
+
padding: 0;
|
|
1389
|
+
background: transparent;
|
|
1390
|
+
border: none;
|
|
1391
|
+
color: var(--text-primary);
|
|
1392
|
+
font-size: var(--font-size-lg);
|
|
1393
|
+
outline: none;
|
|
1394
|
+
}
|
|
1395
|
+
.twm-cmdpal__chips,
|
|
1396
|
+
.twm-cmdpal__toggles {
|
|
1397
|
+
display: flex;
|
|
1398
|
+
flex-wrap: wrap;
|
|
1399
|
+
gap: var(--space-xs);
|
|
1400
|
+
padding: var(--space-sm) var(--space-md);
|
|
1401
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1402
|
+
}
|
|
1403
|
+
.twm-chip {
|
|
1404
|
+
display: inline-flex;
|
|
1405
|
+
align-items: center;
|
|
1406
|
+
gap: var(--space-xs);
|
|
1407
|
+
padding: var(--space-xs) var(--space-sm);
|
|
1408
|
+
background: var(--surface-2);
|
|
1409
|
+
border: 1px solid var(--border-subtle);
|
|
1410
|
+
border-radius: 0;
|
|
1411
|
+
color: var(--text-secondary);
|
|
1412
|
+
font-size: var(--font-size-xs);
|
|
1413
|
+
cursor: pointer;
|
|
1414
|
+
}
|
|
1415
|
+
.twm-chip:hover {
|
|
1416
|
+
background: var(--surface-3);
|
|
1417
|
+
color: var(--text-primary);
|
|
1418
|
+
}
|
|
1419
|
+
.twm-chip--on {
|
|
1420
|
+
background: var(--state-selected);
|
|
1421
|
+
border-color: var(--accent-bright);
|
|
1422
|
+
color: var(--text-bright);
|
|
1423
|
+
}
|
|
1424
|
+
.twm-cmdpal__list {
|
|
1425
|
+
max-height: 40vh;
|
|
1426
|
+
overflow: auto;
|
|
1427
|
+
}
|
|
1428
|
+
.twm-cmdpal__item {
|
|
1429
|
+
display: grid;
|
|
1430
|
+
grid-template-columns: 20px 1fr auto auto;
|
|
1431
|
+
align-items: center;
|
|
1432
|
+
gap: var(--space-sm);
|
|
1433
|
+
padding: var(--space-xs) var(--space-md);
|
|
1434
|
+
cursor: pointer;
|
|
1435
|
+
color: var(--text-secondary);
|
|
1436
|
+
font-size: var(--font-size-sm);
|
|
1437
|
+
}
|
|
1438
|
+
.twm-cmdpal__item:hover,
|
|
1439
|
+
.twm-cmdpal__item--active {
|
|
1440
|
+
background: var(--state-selected);
|
|
1441
|
+
color: var(--text-bright);
|
|
1442
|
+
}
|
|
1443
|
+
.twm-cmdpal__icon { color: var(--text-muted); font-size: 16px; }
|
|
1444
|
+
.twm-cmdpal__label { font-weight: var(--font-weight-medium); }
|
|
1445
|
+
.twm-cmdpal__kind,
|
|
1446
|
+
.twm-cmdpal__hint {
|
|
1447
|
+
color: var(--text-muted);
|
|
1448
|
+
font-size: var(--font-size-xs);
|
|
1449
|
+
}
|
|
1450
|
+
.twm-cmdpal__footer {
|
|
1451
|
+
display: flex;
|
|
1452
|
+
gap: var(--space-md);
|
|
1453
|
+
padding: var(--space-xs) var(--space-md);
|
|
1454
|
+
background: var(--surface-0);
|
|
1455
|
+
border-top: 1px solid var(--border-subtle);
|
|
1456
|
+
color: var(--text-muted);
|
|
1457
|
+
font-size: var(--font-size-xs);
|
|
1458
|
+
}
|
|
1459
|
+
.twm-cmdpal__footer kbd {
|
|
1460
|
+
display: inline-block;
|
|
1461
|
+
padding: 1px 4px;
|
|
1462
|
+
background: var(--surface-2);
|
|
1463
|
+
border: 1px solid var(--border-subtle);
|
|
1464
|
+
border-radius: 0;
|
|
1465
|
+
font-family: var(--font-family-mono);
|
|
1466
|
+
font-size: 10px;
|
|
1467
|
+
margin: 0 1px;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/* ── from tiling/nav.css ───────────────────────── */
|
|
1471
|
+
/* nav.css — left-nav panel-tile (project / files / git). */
|
|
1472
|
+
|
|
1473
|
+
.twm-nav {
|
|
1474
|
+
display: flex;
|
|
1475
|
+
flex-direction: column;
|
|
1476
|
+
height: 100%;
|
|
1477
|
+
background: var(--surface-0);
|
|
1478
|
+
overflow: hidden;
|
|
1479
|
+
}
|
|
1480
|
+
.twm-nav__modes {
|
|
1481
|
+
display: flex;
|
|
1482
|
+
gap: 0;
|
|
1483
|
+
padding: 0;
|
|
1484
|
+
background: var(--surface-1);
|
|
1485
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1486
|
+
}
|
|
1487
|
+
.twm-nav__mode {
|
|
1488
|
+
flex: 1 1 0;
|
|
1489
|
+
display: inline-flex;
|
|
1490
|
+
align-items: center;
|
|
1491
|
+
justify-content: center;
|
|
1492
|
+
gap: 4px;
|
|
1493
|
+
padding: 6px 8px;
|
|
1494
|
+
background: transparent;
|
|
1495
|
+
border: none;
|
|
1496
|
+
border-right: 1px solid var(--border-subtle);
|
|
1497
|
+
border-bottom: 2px solid transparent;
|
|
1498
|
+
border-radius: 0;
|
|
1499
|
+
color: var(--text-secondary);
|
|
1500
|
+
font-size: var(--font-size-xs);
|
|
1501
|
+
cursor: pointer;
|
|
1502
|
+
}
|
|
1503
|
+
.twm-nav__mode:last-child { border-right: none; }
|
|
1504
|
+
.twm-nav__mode:hover { background: var(--state-hover); }
|
|
1505
|
+
.twm-nav__mode--on {
|
|
1506
|
+
background: var(--surface-0);
|
|
1507
|
+
color: var(--text-bright);
|
|
1508
|
+
border-bottom-color: var(--accent-bright);
|
|
1509
|
+
}
|
|
1510
|
+
/* Filter strip — inherits look from sidebar.css's .twm-sidebar-controls
|
|
1511
|
+
* primitive. Just tweak the bottom border to match our pane chrome. */
|
|
1512
|
+
.twm-nav .twm-sidebar-controls.twm-nav__filter {
|
|
1513
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1514
|
+
}
|
|
1515
|
+
.twm-nav__body {
|
|
1516
|
+
flex: 1 1 auto;
|
|
1517
|
+
overflow: hidden;
|
|
1518
|
+
display: flex;
|
|
1519
|
+
min-height: 0;
|
|
1520
|
+
}
|
|
1521
|
+
/* Each mode pane fills the body when visible. Inline style.display
|
|
1522
|
+
* flips between 'flex' and 'none'. */
|
|
1523
|
+
.twm-nav__pane {
|
|
1524
|
+
flex: 1 1 auto;
|
|
1525
|
+
width: 100%;
|
|
1526
|
+
min-width: 0;
|
|
1527
|
+
min-height: 0;
|
|
1528
|
+
flex-direction: column;
|
|
1529
|
+
overflow: hidden;
|
|
1530
|
+
}
|
|
1531
|
+
.twm-nav__table {
|
|
1532
|
+
flex: 1 1 auto;
|
|
1533
|
+
width: 100%;
|
|
1534
|
+
overflow: auto;
|
|
1535
|
+
min-height: 0;
|
|
1536
|
+
}
|
|
1537
|
+
/* Force the file-tree section's content to be expanded since there is
|
|
1538
|
+
* no longer a header to toggle it. */
|
|
1539
|
+
.twm-nav__pane--files .twm-collapsible-content {
|
|
1540
|
+
display: block !important;
|
|
1541
|
+
}
|
|
1542
|
+
/* Action row above the file list — hosts the single "New file" button. */
|
|
1543
|
+
.twm-nav__files-actions {
|
|
1544
|
+
display: flex;
|
|
1545
|
+
gap: 4px;
|
|
1546
|
+
padding: 6px 6px 4px;
|
|
1547
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
1548
|
+
flex: 0 0 auto;
|
|
1549
|
+
}
|
|
1550
|
+
.twm-nav__files-mount {
|
|
1551
|
+
flex: 1 1 auto;
|
|
1552
|
+
min-height: 0;
|
|
1553
|
+
overflow: hidden;
|
|
1554
|
+
display: flex;
|
|
1555
|
+
flex-direction: column;
|
|
1556
|
+
}
|
|
1557
|
+
/* Reset <ul.twm-tree-view> browser defaults so list items render at the
|
|
1558
|
+
* left edge of the body instead of with the 40px UA padding. */
|
|
1559
|
+
.twm-nav__pane ul.twm-tree-view {
|
|
1560
|
+
list-style: none;
|
|
1561
|
+
margin: 0;
|
|
1562
|
+
padding: 0;
|
|
1563
|
+
}
|
|
1564
|
+
.twm-nav__pane .twm-tree-item {
|
|
1565
|
+
/* keep the .tree-item padding from main_new.css for nested chevron */
|
|
1566
|
+
}
|
|
1567
|
+
.twm-nav__icon {
|
|
1568
|
+
font-size: 14px;
|
|
1569
|
+
color: var(--text-muted);
|
|
1570
|
+
vertical-align: middle;
|
|
1571
|
+
}
|
|
1572
|
+
.twm-nav__error {
|
|
1573
|
+
padding: 12px;
|
|
1574
|
+
color: var(--color-danger);
|
|
1575
|
+
font-size: var(--font-size-xs);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/* ── from tiling/host.css ───────────────────────── */
|
|
1579
|
+
/* host.css — body layout for the tiling shell.
|
|
1580
|
+
*
|
|
1581
|
+
* The Ecosim bootstrap builds .twm-global-top-bar + .container + .twm-global-bottom-bar.
|
|
1582
|
+
* Our installer removes .container and inserts .twm-host between the
|
|
1583
|
+
* top and bottom bars; this stylesheet makes the body a flex column so
|
|
1584
|
+
* the WM host fills the remaining space.
|
|
1585
|
+
*/
|
|
1586
|
+
|
|
1587
|
+
/* Body is a flex column: top bar, WM host (fill), bottom bar. We
|
|
1588
|
+
* override the legacy `position: fixed` on both bars so they become
|
|
1589
|
+
* deterministic flex items and the host's height is exactly the
|
|
1590
|
+
* leftover space — no math, no overlap, no off-by-N pixels. Their
|
|
1591
|
+
* `z-index` stays put for dropdown stacking. */
|
|
1592
|
+
body.twm-body {
|
|
1593
|
+
display: flex;
|
|
1594
|
+
flex-direction: column;
|
|
1595
|
+
margin: 0;
|
|
1596
|
+
height: 100vh;
|
|
1597
|
+
width: 100vw;
|
|
1598
|
+
overflow: hidden;
|
|
1599
|
+
}
|
|
1600
|
+
body.twm-body > .twm-global-top-bar,
|
|
1601
|
+
body.twm-body > .twm-global-bottom-bar {
|
|
1602
|
+
position: relative !important;
|
|
1603
|
+
inset: auto !important;
|
|
1604
|
+
flex: 0 0 auto;
|
|
1605
|
+
}
|
|
1606
|
+
body.twm-body > .twm-host {
|
|
1607
|
+
flex: 1 1 auto;
|
|
1608
|
+
min-height: 0;
|
|
1609
|
+
min-width: 0;
|
|
1610
|
+
display: flex;
|
|
1611
|
+
overflow: hidden;
|
|
1612
|
+
background: var(--surface-app);
|
|
1613
|
+
}
|
|
1614
|
+
/* Sim controls relocated into .twm-global-top-bar .twm-bar-right — they came
|
|
1615
|
+
* from .workspace-top-bar where the layout was different. Strip the
|
|
1616
|
+
* extra padding so they sit next to the panel-toggles cleanly. */
|
|
1617
|
+
.twm-global-top-bar .twm-sim-controls {
|
|
1618
|
+
display: inline-flex;
|
|
1619
|
+
align-items: center;
|
|
1620
|
+
gap: 4px;
|
|
1621
|
+
margin-right: 8px;
|
|
1622
|
+
padding: 0;
|
|
1623
|
+
}
|
|
1624
|
+
.twm-global-top-bar .twm-sim-controls .twm-menu-bar {
|
|
1625
|
+
display: inline-flex;
|
|
1626
|
+
align-items: center;
|
|
1627
|
+
gap: 2px;
|
|
1628
|
+
padding: 0;
|
|
1629
|
+
background: transparent;
|
|
1630
|
+
border: none;
|
|
1631
|
+
}
|
|
1632
|
+
.twm-global-top-bar .twm-sim-controls button {
|
|
1633
|
+
background: transparent;
|
|
1634
|
+
border: 1px solid transparent;
|
|
1635
|
+
border-radius: 0;
|
|
1636
|
+
color: var(--text-secondary);
|
|
1637
|
+
}
|
|
1638
|
+
.twm-global-top-bar .twm-sim-controls button:hover {
|
|
1639
|
+
background: var(--state-hover);
|
|
1640
|
+
color: var(--text-primary);
|
|
1641
|
+
}
|
|
1642
|
+
/* Desktop bar (mounted in global-bottom-bar .twm-bar-right). */
|
|
1643
|
+
.twm-desktops {
|
|
1644
|
+
display: inline-flex;
|
|
1645
|
+
align-items: center;
|
|
1646
|
+
gap: 2px;
|
|
1647
|
+
margin: 0 8px;
|
|
1648
|
+
}
|
|
1649
|
+
.twm-desk__btn {
|
|
1650
|
+
display: inline-flex;
|
|
1651
|
+
align-items: center;
|
|
1652
|
+
justify-content: center;
|
|
1653
|
+
min-width: 22px;
|
|
1654
|
+
height: 18px;
|
|
1655
|
+
padding: 0 6px;
|
|
1656
|
+
background: transparent;
|
|
1657
|
+
border: 1px solid var(--border-subtle);
|
|
1658
|
+
border-radius: 0;
|
|
1659
|
+
color: var(--text-secondary);
|
|
1660
|
+
font-family: var(--font-family-mono);
|
|
1661
|
+
font-size: var(--font-size-xs);
|
|
1662
|
+
cursor: pointer;
|
|
1663
|
+
}
|
|
1664
|
+
.twm-desk__btn:hover { background: var(--state-hover); }
|
|
1665
|
+
.twm-desk__btn--on {
|
|
1666
|
+
background: var(--state-selected);
|
|
1667
|
+
color: var(--text-bright);
|
|
1668
|
+
border-color: var(--accent-bright);
|
|
1669
|
+
}
|
|
1670
|
+
.twm-desk__btn--add { color: var(--text-muted); }
|
|
1671
|
+
.twm-panel-toggle-btn--on {
|
|
1672
|
+
background: var(--state-selected) !important;
|
|
1673
|
+
color: var(--text-bright) !important;
|
|
1674
|
+
}
|
|
1675
|
+
/* Hamburger button — left of the EcoAgent brand. Collapses File/Edit/
|
|
1676
|
+
* Help into a single popup so the top bar reads as one row of icons
|
|
1677
|
+
* instead of three text dropdowns competing with the page shortcuts. */
|
|
1678
|
+
.twm-hamburger-btn {
|
|
1679
|
+
display: inline-flex;
|
|
1680
|
+
align-items: center;
|
|
1681
|
+
justify-content: center;
|
|
1682
|
+
width: 35px;
|
|
1683
|
+
height: 35px;
|
|
1684
|
+
padding: 0;
|
|
1685
|
+
background: transparent;
|
|
1686
|
+
border: none;
|
|
1687
|
+
border-radius: 0;
|
|
1688
|
+
color: var(--text-secondary);
|
|
1689
|
+
cursor: pointer;
|
|
1690
|
+
}
|
|
1691
|
+
.twm-hamburger-btn:hover {
|
|
1692
|
+
background: var(--state-hover);
|
|
1693
|
+
color: var(--text-primary);
|
|
1694
|
+
}
|
|
1695
|
+
/* Hide the original File/Edit/Help dropdowns — they live inside the
|
|
1696
|
+
* hamburger menu now. Their `.menu-entry` buttons stay in the DOM so
|
|
1697
|
+
* the hamburger can trigger them via .click(). */
|
|
1698
|
+
.twm-global-top-bar .twm-menu-item.twm-hidden-menu-item {
|
|
1699
|
+
display: none !important;
|
|
1700
|
+
}
|
|
1701
|
+
/* The agent + flow tabs each shipped their own "Pop out" button into a
|
|
1702
|
+
* floating slide-out panel — superseded by the WM's Alt+F / promote-
|
|
1703
|
+
* to-window. Hide them so the chrome doesn't carry two parallel
|
|
1704
|
+
* affordances for the same action. */
|
|
1705
|
+
/* Catch-all: any pop-out trigger inside any page tile is hidden. The
|
|
1706
|
+
* tiling shell handles "pop a tile out into a floating window" via
|
|
1707
|
+
* Alt+F / the chrome promote button, so per-page pop-out buttons are
|
|
1708
|
+
* always redundant. */
|
|
1709
|
+
.twm-page-shell [data-action="popout"],
|
|
1710
|
+
.twm-page-shell #ea-archetype-popout,
|
|
1711
|
+
.twm-page-shell #ea-sector-popout {
|
|
1712
|
+
display: none !important;
|
|
1713
|
+
}
|
|
1714
|
+
/* Inline row-action buttons (edit / delete) on navigation tables. */
|
|
1715
|
+
.twm-row-action {
|
|
1716
|
+
display: inline-flex;
|
|
1717
|
+
align-items: center;
|
|
1718
|
+
justify-content: center;
|
|
1719
|
+
width: 22px;
|
|
1720
|
+
height: 22px;
|
|
1721
|
+
padding: 0;
|
|
1722
|
+
margin: 0 1px;
|
|
1723
|
+
background: transparent;
|
|
1724
|
+
border: 1px solid transparent;
|
|
1725
|
+
border-radius: 0;
|
|
1726
|
+
color: var(--text-muted);
|
|
1727
|
+
cursor: pointer;
|
|
1728
|
+
opacity: 0;
|
|
1729
|
+
transition: opacity 80ms ease, color 80ms ease, background 80ms ease;
|
|
1730
|
+
}
|
|
1731
|
+
tr:hover .twm-row-action { opacity: 1; }
|
|
1732
|
+
.twm-row-action:hover {
|
|
1733
|
+
color: var(--text-primary);
|
|
1734
|
+
background: var(--state-hover);
|
|
1735
|
+
}
|
|
1736
|
+
.twm-row-action--danger:hover {
|
|
1737
|
+
color: var(--text-bright);
|
|
1738
|
+
background: var(--color-danger);
|
|
1739
|
+
}
|
|
1740
|
+
.twm-row-actions-cell { white-space: nowrap; text-align: right; }
|
|
1741
|
+
/* Hierarchical landing rows (Markets, Agents) — archetype rows carry
|
|
1742
|
+
* a chevron + entity icon; instance rows are indented underneath. */
|
|
1743
|
+
.twm-market-row {
|
|
1744
|
+
display: inline-flex;
|
|
1745
|
+
align-items: center;
|
|
1746
|
+
gap: 6px;
|
|
1747
|
+
width: 100%;
|
|
1748
|
+
}
|
|
1749
|
+
.twm-market-row__icon {
|
|
1750
|
+
font-size: 14px;
|
|
1751
|
+
color: var(--text-muted);
|
|
1752
|
+
}
|
|
1753
|
+
.twm-market-row--archetype .twm-market-row__icon {
|
|
1754
|
+
color: var(--text-primary);
|
|
1755
|
+
}
|
|
1756
|
+
.twm-market-row__label {
|
|
1757
|
+
flex: 1 1 auto;
|
|
1758
|
+
min-width: 0;
|
|
1759
|
+
overflow: hidden;
|
|
1760
|
+
text-overflow: ellipsis;
|
|
1761
|
+
white-space: nowrap;
|
|
1762
|
+
}
|
|
1763
|
+
.twm-market-chevron {
|
|
1764
|
+
display: inline-flex;
|
|
1765
|
+
align-items: center;
|
|
1766
|
+
justify-content: center;
|
|
1767
|
+
width: 18px;
|
|
1768
|
+
height: 18px;
|
|
1769
|
+
padding: 0;
|
|
1770
|
+
background: transparent;
|
|
1771
|
+
border: none;
|
|
1772
|
+
color: var(--text-muted);
|
|
1773
|
+
cursor: pointer;
|
|
1774
|
+
}
|
|
1775
|
+
.twm-market-chevron:hover { color: var(--text-primary); }
|
|
1776
|
+
.twm-market-row--instance .twm-market-row__indent {
|
|
1777
|
+
display: inline-block;
|
|
1778
|
+
width: 22px;
|
|
1779
|
+
flex: 0 0 22px;
|
|
1780
|
+
}
|
|
1781
|
+
.twm-market-row--instance .twm-market-row__label {
|
|
1782
|
+
color: var(--text-secondary);
|
|
1783
|
+
}
|
|
1784
|
+
.twm-market-row__count {
|
|
1785
|
+
display: inline-block;
|
|
1786
|
+
margin-left: 6px;
|
|
1787
|
+
padding: 0 5px;
|
|
1788
|
+
color: var(--text-muted);
|
|
1789
|
+
font-size: 11px;
|
|
1790
|
+
border: 1px solid var(--border-subtle);
|
|
1791
|
+
line-height: 1.5;
|
|
1792
|
+
}
|
|
1793
|
+
.twm-market-chevron:disabled {
|
|
1794
|
+
opacity: 0.25;
|
|
1795
|
+
cursor: default;
|
|
1796
|
+
}
|
|
1797
|
+
/* DataTable filter strip stickiness — the legacy rule for this lived
|
|
1798
|
+
* only inside `.twm-preview-table-wrap--wizard`; generalize it so every
|
|
1799
|
+
* DataTable (landings, instance lists, …) keeps its filter row + column
|
|
1800
|
+
* headers visible when the body scrolls. Both rows need opaque
|
|
1801
|
+
* backgrounds + z-index above the data cells so rows can't bleed
|
|
1802
|
+
* through. */
|
|
1803
|
+
/* Structural split: thead lives in its own non-scrolling header wrap
|
|
1804
|
+
* above the scrollable body wrap. The header gets opaque chrome so it
|
|
1805
|
+
* reads as a single bar with the body; no `position: sticky` is
|
|
1806
|
+
* needed anymore since the header literally doesn't scroll. */
|
|
1807
|
+
.twm-data-table-component .twm-preview-table-header-wrap {
|
|
1808
|
+
background: var(--surface-1);
|
|
1809
|
+
border-bottom: 1px solid var(--border-strong);
|
|
1810
|
+
}
|
|
1811
|
+
.twm-data-table-component .twm-preview-table-header-wrap .twm-preview-table thead th {
|
|
1812
|
+
background: var(--surface-1);
|
|
1813
|
+
}
|
|
1814
|
+
.twm-data-table-component .twm-preview-table-header-wrap .twm-preview-table .twm-data-table__filter-row th {
|
|
1815
|
+
background: var(--surface-0);
|
|
1816
|
+
}
|
|
1817
|
+
/* Compact mode (P2) — used by the bottom-panel Registries tab.
|
|
1818
|
+
* Tighter padding + smaller font matches the surrounding bottom-panel
|
|
1819
|
+
* chrome (run console, bus, query) and gets ~30% more rows visible
|
|
1820
|
+
* in the same vertical space. Opt-in via `mode: 'compact'`. */
|
|
1821
|
+
.twm-data-table-component--compact {
|
|
1822
|
+
font-size: 11px;
|
|
1823
|
+
}
|
|
1824
|
+
.twm-data-table-component--compact .twm-preview-table th,
|
|
1825
|
+
.twm-data-table-component--compact .twm-preview-table td {
|
|
1826
|
+
padding: 2px 6px;
|
|
1827
|
+
line-height: 1.35;
|
|
1828
|
+
}
|
|
1829
|
+
.twm-data-table-component--compact .twm-pagination-controls {
|
|
1830
|
+
padding: 1px 6px !important;
|
|
1831
|
+
font-size: 10px !important;
|
|
1832
|
+
/* Tighter gap so pager reads as one strip, like the category /
|
|
1833
|
+
* schema filter bar above the table — not as a spaced toolbar. */
|
|
1834
|
+
gap: 4px !important;
|
|
1835
|
+
background: #181818;
|
|
1836
|
+
border-top: 1px solid var(--ea-border, #2a2a2a);
|
|
1837
|
+
border-bottom: 1px solid var(--ea-border, #2a2a2a);
|
|
1838
|
+
}
|
|
1839
|
+
.twm-data-table-component--compact .twm-data-table__filter-input {
|
|
1840
|
+
font-size: 10px;
|
|
1841
|
+
padding: 1px 4px;
|
|
1842
|
+
}
|
|
1843
|
+
/* Match the narrow chrome of the filter strip (.ea-bp-browser__cat):
|
|
1844
|
+
* 20px height, border-no-radius, transparent background, uppercase
|
|
1845
|
+
* typography. So the pager reads as a sibling of the category /
|
|
1846
|
+
* schema bar instead of a heavier toolbar.
|
|
1847
|
+
*
|
|
1848
|
+
* `!important` is used liberally below because DataTable.js still
|
|
1849
|
+
* sets per-button / per-input chrome via inline `style.cssText` —
|
|
1850
|
+
* inline beats CSS without `!important`. Once those inline styles
|
|
1851
|
+
* migrate to CSS classes we can drop the `!important`s. */
|
|
1852
|
+
.twm-data-table-component--compact .twm-pagination-controls .twm-pagination-btn {
|
|
1853
|
+
min-width: 22px !important;
|
|
1854
|
+
height: 20px !important;
|
|
1855
|
+
padding: 0 6px !important;
|
|
1856
|
+
background: transparent !important;
|
|
1857
|
+
border: 1px solid var(--ea-border, #2a2a2a) !important;
|
|
1858
|
+
border-radius: 0 !important;
|
|
1859
|
+
color: var(--ea-text-dim, #aaa) !important;
|
|
1860
|
+
font-size: 10px !important;
|
|
1861
|
+
text-transform: uppercase;
|
|
1862
|
+
letter-spacing: 0.04em;
|
|
1863
|
+
}
|
|
1864
|
+
.twm-data-table-component--compact .twm-pagination-controls .twm-pagination-btn:hover:not(:disabled) {
|
|
1865
|
+
color: var(--ea-text, #ddd) !important;
|
|
1866
|
+
border-color: var(--ea-accent, #1177b3) !important;
|
|
1867
|
+
}
|
|
1868
|
+
/* Text labels inside the pager — the "Rows X–Y of Z" and "Page X of
|
|
1869
|
+
* Y" spans don't have classes (just inline style), so target every
|
|
1870
|
+
* direct-span child of the controls to neutralise their inline color
|
|
1871
|
+
* + font-size + add the uppercase look from the filter strip. */
|
|
1872
|
+
/* Loading spinner — `setLoading(true)` (or returning a Promise from
|
|
1873
|
+
* onPageChange/onSort) overlays a translucent layer + a CSS spinner
|
|
1874
|
+
* over the whole table. Wrapper gets `position: relative` set
|
|
1875
|
+
* inline by the JS so the absolute overlay anchors correctly. */
|
|
1876
|
+
.twm-data-table__loading {
|
|
1877
|
+
position: absolute;
|
|
1878
|
+
inset: 0;
|
|
1879
|
+
z-index: 10;
|
|
1880
|
+
display: flex;
|
|
1881
|
+
align-items: center;
|
|
1882
|
+
justify-content: center;
|
|
1883
|
+
/* Translucent so the user still sees the stale rows underneath
|
|
1884
|
+
* and can recognise the table — the spinner says "fresh data
|
|
1885
|
+
* coming", not "blank". */
|
|
1886
|
+
background: rgba(20, 20, 20, 0.45);
|
|
1887
|
+
backdrop-filter: blur(1px);
|
|
1888
|
+
pointer-events: all; /* swallow clicks while in-flight */
|
|
1889
|
+
cursor: progress;
|
|
1890
|
+
}
|
|
1891
|
+
.twm-data-table__spinner {
|
|
1892
|
+
width: 28px;
|
|
1893
|
+
height: 28px;
|
|
1894
|
+
border: 2px solid rgba(255, 255, 255, 0.18);
|
|
1895
|
+
border-top-color: var(--ea-accent, #1177b3);
|
|
1896
|
+
border-radius: 50%;
|
|
1897
|
+
animation: data-table__spin 0.7s linear infinite;
|
|
1898
|
+
}
|
|
1899
|
+
.twm-data-table-component--compact .twm-pagination-controls > span {
|
|
1900
|
+
color: var(--ea-text-dim, #aaa) !important;
|
|
1901
|
+
font-size: 9.5px !important;
|
|
1902
|
+
text-transform: uppercase;
|
|
1903
|
+
letter-spacing: 0.04em;
|
|
1904
|
+
}
|
|
1905
|
+
/* Sticky-cell rules removed — header now lives in its own non-
|
|
1906
|
+
* scrolling container after the structural split. */
|
|
1907
|
+
|
|
1908
|
+
/* The legacy `.twm-landing__table` host has its own `overflow:auto`,
|
|
1909
|
+
* which fights with DataTable's inner `.twm-preview-table-wrap` scroll
|
|
1910
|
+
* container. When the outer scroll wins, the sticky filter row is
|
|
1911
|
+
* computed against the wrong ancestor and stops sticking — rows
|
|
1912
|
+
* scroll past the filter strip visually. Defer all scrolling to the
|
|
1913
|
+
* inner wrap. Same fix for the navigator's table host. */
|
|
1914
|
+
.twm-landing__table,
|
|
1915
|
+
.twm-nav__table {
|
|
1916
|
+
overflow: hidden !important;
|
|
1917
|
+
}
|
|
1918
|
+
.twm-landing__table > .twm-data-table-component,
|
|
1919
|
+
.twm-nav__table > .twm-data-table-component {
|
|
1920
|
+
flex: 1 1 auto;
|
|
1921
|
+
min-height: 0;
|
|
1922
|
+
}
|
|
1923
|
+
/* main_new.css caps every `.twm-preview-table-wrap` at 300px so the legacy
|
|
1924
|
+
* pre-tiling pages stayed compact. Inside the tiling shell, the wrap is
|
|
1925
|
+
* the SOLE scrolling surface for the landing table — capping it at
|
|
1926
|
+
* 300px clips the list to ~8 rows. Force the wrap to fill its flex
|
|
1927
|
+
* parent and rely on its own `overflow: auto` (set inline by DataTable)
|
|
1928
|
+
* for scrolling. */
|
|
1929
|
+
.twm-landing__table .twm-preview-table-wrap,
|
|
1930
|
+
.twm-nav__table .twm-preview-table-wrap {
|
|
1931
|
+
max-height: none !important;
|
|
1932
|
+
height: auto;
|
|
1933
|
+
flex: 1 1 0;
|
|
1934
|
+
min-height: 0;
|
|
1935
|
+
}
|
|
1936
|
+
/* Filter-row sticky rule removed — the row lives inside the non-
|
|
1937
|
+
* scrolling header wrap after the structural split, so no sticky
|
|
1938
|
+
* positioning is needed and the `top: 32px` magic-offset (which
|
|
1939
|
+
* broke in compact mode) is gone. */
|
|
1940
|
+
|
|
1941
|
+
/* Navigation-table density — tighten row padding for landings + the
|
|
1942
|
+
* navigator. The default DataTable padding of `var(--space-sm) 8px` is
|
|
1943
|
+
* comfortable for data-viewing tables; nav tables benefit from being
|
|
1944
|
+
* denser so more items fit. */
|
|
1945
|
+
.twm-landing .twm-data-table-component .twm-preview-table td,
|
|
1946
|
+
.twm-landing .twm-data-table-component .twm-preview-table th,
|
|
1947
|
+
.twm-nav__table .twm-data-table-component .twm-preview-table td,
|
|
1948
|
+
.twm-nav__table .twm-data-table-component .twm-preview-table th {
|
|
1949
|
+
padding: 2px 8px;
|
|
1950
|
+
}
|
|
1951
|
+
.twm-landing .twm-data-table-component .twm-preview-table .twm-data-table__filter-row th,
|
|
1952
|
+
.twm-nav__table .twm-data-table-component .twm-preview-table .twm-data-table__filter-row th {
|
|
1953
|
+
padding: 3px 4px;
|
|
1954
|
+
/* Column header in these tables is ~22 px tall (font + 2×2 padding
|
|
1955
|
+
* + 1 px border). The default filter-row `top: 32px` left a ~10 px
|
|
1956
|
+
* gap rows scrolled through. Snap the filter row right up against
|
|
1957
|
+
* the shorter header. */
|
|
1958
|
+
top: 22px;
|
|
1959
|
+
}
|
|
1960
|
+
/* Breadcrumb strip — vertically center content. */
|
|
1961
|
+
.twm-page-shell__breadcrumb {
|
|
1962
|
+
display: flex;
|
|
1963
|
+
align-items: center;
|
|
1964
|
+
}
|
|
1965
|
+
.twm-page-shell__breadcrumb .twm-tile-breadcrumb {
|
|
1966
|
+
flex: 1 1 auto;
|
|
1967
|
+
min-width: 0;
|
|
1968
|
+
line-height: 1;
|
|
1969
|
+
}
|
|
1970
|
+
.twm-page-shell__breadcrumb .twm-topbar-breadcrumb__list {
|
|
1971
|
+
display: inline-flex;
|
|
1972
|
+
align-items: center;
|
|
1973
|
+
margin: 0;
|
|
1974
|
+
padding: 0;
|
|
1975
|
+
list-style: none;
|
|
1976
|
+
gap: 4px;
|
|
1977
|
+
}
|
|
1978
|
+
.twm-page-shell__breadcrumb .twm-topbar-breadcrumb__item,
|
|
1979
|
+
.twm-page-shell__breadcrumb .twm-topbar-breadcrumb__link {
|
|
1980
|
+
display: inline-flex;
|
|
1981
|
+
align-items: center;
|
|
1982
|
+
gap: 4px;
|
|
1983
|
+
}
|
|
1984
|
+
/* Modal dialog styling lives in ecoagent_extra_modes.css under
|
|
1985
|
+
* `.twm-managed-window--modal`. Removed legacy in-host block — was a
|
|
1986
|
+
* competing `display: flex; flex-direction: column` that stacked
|
|
1987
|
+
* labels above fields and fought the canonical grid layout. */
|
|
1988
|
+
|
|
1989
|
+
/* The Object Explorer's host expects `.panel.right .right-panel-body`
|
|
1990
|
+
* — we recreate that shell inside the panel:right tile so the existing
|
|
1991
|
+
* module mounts unchanged. The legacy `.panel.right` layout rules
|
|
1992
|
+
* (fixed width, position, etc.) would fight the tile sizing, so we
|
|
1993
|
+
* reset to a plain stretchy column. */
|
|
1994
|
+
.twm-leaf__body .twm-panel-right {
|
|
1995
|
+
position: static;
|
|
1996
|
+
width: 100%;
|
|
1997
|
+
height: 100%;
|
|
1998
|
+
flex: 1 1 auto;
|
|
1999
|
+
min-width: 0;
|
|
2000
|
+
min-height: 0;
|
|
2001
|
+
display: flex;
|
|
2002
|
+
flex-direction: column;
|
|
2003
|
+
background: transparent;
|
|
2004
|
+
box-shadow: none;
|
|
2005
|
+
overflow: hidden;
|
|
2006
|
+
}
|
|
2007
|
+
/* Page-shell: every content tile gets a breadcrumb strip on top and a
|
|
2008
|
+
* scrollable content area below. */
|
|
2009
|
+
.twm-page-shell {
|
|
2010
|
+
display: flex;
|
|
2011
|
+
flex-direction: column;
|
|
2012
|
+
height: 100%;
|
|
2013
|
+
overflow: hidden;
|
|
2014
|
+
}
|
|
2015
|
+
/* Lock the breadcrumb strip to a constant height across every page
|
|
2016
|
+
* so the chrome doesn't visibly shift when switching between kinds
|
|
2017
|
+
* with different segment counts. */
|
|
2018
|
+
.twm-page-shell__breadcrumb {
|
|
2019
|
+
flex: 0 0 26px;
|
|
2020
|
+
min-height: 26px;
|
|
2021
|
+
max-height: 26px;
|
|
2022
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
2023
|
+
background: var(--surface-1);
|
|
2024
|
+
overflow: hidden;
|
|
2025
|
+
}
|
|
2026
|
+
.twm-page-shell__breadcrumb .twm-tile-breadcrumb {
|
|
2027
|
+
display: flex;
|
|
2028
|
+
align-items: center;
|
|
2029
|
+
gap: 6px;
|
|
2030
|
+
padding: 2px 8px;
|
|
2031
|
+
line-height: 22px;
|
|
2032
|
+
}
|
|
2033
|
+
.twm-page-shell__content {
|
|
2034
|
+
flex: 1 1 auto;
|
|
2035
|
+
min-height: 0;
|
|
2036
|
+
min-width: 0;
|
|
2037
|
+
/* No `overflow: auto` here — each mounted page owns its own
|
|
2038
|
+
* scroll region (e.g. .ea-agent-editor's `.nb-structured-editor__content`).
|
|
2039
|
+
* Allowing the wrapper to scroll on top of that produced double
|
|
2040
|
+
* scrollbars + a shimmy on sub-tab switches inside agent / market
|
|
2041
|
+
* pages (Settings vs code). */
|
|
2042
|
+
overflow: hidden;
|
|
2043
|
+
display: flex;
|
|
2044
|
+
flex-direction: column;
|
|
2045
|
+
/* Children that need to layer over the content slot (the loading
|
|
2046
|
+
* overlay) anchor against this. */
|
|
2047
|
+
position: relative;
|
|
2048
|
+
}
|
|
2049
|
+
/* Loading overlay — shown while a page's async mount() is in flight.
|
|
2050
|
+
* `--pending` is the pre-show state set for the first 80ms so cache
|
|
2051
|
+
* hits / instant renders never strobe a spinner. After that the
|
|
2052
|
+
* overlay becomes visible until the mount promise resolves. */
|
|
2053
|
+
.twm-tile-loading {
|
|
2054
|
+
position: absolute;
|
|
2055
|
+
inset: 0;
|
|
2056
|
+
display: flex;
|
|
2057
|
+
align-items: center;
|
|
2058
|
+
justify-content: center;
|
|
2059
|
+
background: var(--ea-bg-content, #1a1a1a);
|
|
2060
|
+
z-index: 5;
|
|
2061
|
+
opacity: 1;
|
|
2062
|
+
transition: opacity 120ms ease-out;
|
|
2063
|
+
}
|
|
2064
|
+
.twm-tile-loading--pending {
|
|
2065
|
+
opacity: 0;
|
|
2066
|
+
pointer-events: none;
|
|
2067
|
+
}
|
|
2068
|
+
.twm-tile-loading__spinner {
|
|
2069
|
+
width: 18px;
|
|
2070
|
+
height: 18px;
|
|
2071
|
+
border: 2px solid var(--ea-border, #333);
|
|
2072
|
+
border-top-color: var(--accent, #4a90e2);
|
|
2073
|
+
border-radius: 50%;
|
|
2074
|
+
animation: twm-tile-loading-spin 0.7s linear infinite;
|
|
2075
|
+
}
|
|
2076
|
+
/* Merged home page — detail header row, then the project-setup body
|
|
2077
|
+
* fills the rest of the tile. */
|
|
2078
|
+
.twm-home-page {
|
|
2079
|
+
display: flex;
|
|
2080
|
+
flex-direction: column;
|
|
2081
|
+
height: 100%;
|
|
2082
|
+
overflow: hidden;
|
|
2083
|
+
}
|
|
2084
|
+
.twm-home-page__header {
|
|
2085
|
+
flex: 0 0 auto;
|
|
2086
|
+
}
|
|
2087
|
+
.twm-home-page__body {
|
|
2088
|
+
flex: 1 1 auto;
|
|
2089
|
+
min-height: 0;
|
|
2090
|
+
overflow: auto;
|
|
2091
|
+
display: flex;
|
|
2092
|
+
flex-direction: column;
|
|
2093
|
+
}
|
|
2094
|
+
/* Detail header — uniform height + padding across every tab. */
|
|
2095
|
+
.twm-page-shell__content .twm-entity-header {
|
|
2096
|
+
min-height: 32px;
|
|
2097
|
+
padding: 4px 12px !important;
|
|
2098
|
+
margin: 0 !important;
|
|
2099
|
+
background: var(--surface-1);
|
|
2100
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
2101
|
+
}
|
|
2102
|
+
/* Bottom panel mounted as a tile — reset the legacy fixed/absolute
|
|
2103
|
+
* layout `.left-bottom` carries from main_new.css so it stretches to
|
|
2104
|
+
* fill the tile body. */
|
|
2105
|
+
.twm-leaf__body .twm-panel-bottom {
|
|
2106
|
+
position: static;
|
|
2107
|
+
height: 100%;
|
|
2108
|
+
width: 100%;
|
|
2109
|
+
display: flex;
|
|
2110
|
+
flex-direction: column;
|
|
2111
|
+
overflow: hidden;
|
|
2112
|
+
}
|
|
2113
|
+
.twm-leaf__body .twm-panel-bottom #bottom-tabs-content {
|
|
2114
|
+
flex: 1 1 auto;
|
|
2115
|
+
min-height: 0;
|
|
2116
|
+
overflow: auto;
|
|
2117
|
+
}
|
|
2118
|
+
.twm-bp-loading {
|
|
2119
|
+
padding: 14px;
|
|
2120
|
+
color: var(--text-muted);
|
|
2121
|
+
font-size: var(--font-size-xs);
|
|
2122
|
+
}
|
|
2123
|
+
.twm-bp-loading--err { color: var(--color-danger); }
|
|
2124
|
+
/* Self-contained bottom panel — sole owner of the panel:bottom tile.
|
|
2125
|
+
* Tab strip on top, body below; both painted synchronously by the
|
|
2126
|
+
* tiling shell so the panel can never end up visually empty. */
|
|
2127
|
+
.twm-bp {
|
|
2128
|
+
display: flex;
|
|
2129
|
+
flex-direction: column;
|
|
2130
|
+
height: 100%;
|
|
2131
|
+
width: 100%;
|
|
2132
|
+
overflow: hidden;
|
|
2133
|
+
background: var(--surface-0);
|
|
2134
|
+
color: var(--text-body);
|
|
2135
|
+
}
|
|
2136
|
+
/* Tab strip matches the legacy `.bottom-tabs-header` look: light row
|
|
2137
|
+
* with no per-tab background, uppercase labels spaced by left margin,
|
|
2138
|
+
* white underline on the active tab. No boxy buttons. */
|
|
2139
|
+
.twm-bp__tabs {
|
|
2140
|
+
flex: 0 0 auto;
|
|
2141
|
+
display: flex;
|
|
2142
|
+
align-items: center;
|
|
2143
|
+
gap: 0;
|
|
2144
|
+
padding: 0 0 0 4px;
|
|
2145
|
+
background: var(--surface-0);
|
|
2146
|
+
border-bottom: 1px solid #292929;
|
|
2147
|
+
overflow-x: auto;
|
|
2148
|
+
overflow-y: hidden;
|
|
2149
|
+
scrollbar-width: none;
|
|
2150
|
+
white-space: nowrap;
|
|
2151
|
+
}
|
|
2152
|
+
.twm-bp__tabs::-webkit-scrollbar { width: 0; height: 0; display: none; }
|
|
2153
|
+
.twm-bp__tab {
|
|
2154
|
+
display: inline-flex;
|
|
2155
|
+
align-items: center;
|
|
2156
|
+
gap: 0;
|
|
2157
|
+
height: 30px;
|
|
2158
|
+
line-height: 30px;
|
|
2159
|
+
margin-left: 10px;
|
|
2160
|
+
padding: 0;
|
|
2161
|
+
background: transparent;
|
|
2162
|
+
border: none;
|
|
2163
|
+
border-bottom: 1px solid transparent;
|
|
2164
|
+
color: inherit;
|
|
2165
|
+
text-transform: uppercase;
|
|
2166
|
+
font-size: var(--font-size-xs);
|
|
2167
|
+
cursor: pointer;
|
|
2168
|
+
user-select: none;
|
|
2169
|
+
}
|
|
2170
|
+
.twm-bp__tab:hover { background: transparent; color: var(--text-bright); }
|
|
2171
|
+
.twm-bp__tab--on {
|
|
2172
|
+
color: var(--text-bright);
|
|
2173
|
+
border-bottom-color: #fff;
|
|
2174
|
+
}
|
|
2175
|
+
.twm-bp__body {
|
|
2176
|
+
flex: 1 1 auto;
|
|
2177
|
+
min-height: 0;
|
|
2178
|
+
overflow: auto;
|
|
2179
|
+
}
|
|
2180
|
+
.twm-bp-pane {
|
|
2181
|
+
padding: 6px 10px;
|
|
2182
|
+
}
|
|
2183
|
+
.twm-bp-pane__placeholder {
|
|
2184
|
+
padding: 12px;
|
|
2185
|
+
color: var(--text-muted);
|
|
2186
|
+
font-size: var(--font-size-xs);
|
|
2187
|
+
}
|
|
2188
|
+
.twm-bp-log {
|
|
2189
|
+
width: 100%;
|
|
2190
|
+
border-collapse: collapse;
|
|
2191
|
+
font-family: var(--font-family-mono);
|
|
2192
|
+
font-size: var(--font-size-xs);
|
|
2193
|
+
}
|
|
2194
|
+
.twm-bp-log thead th {
|
|
2195
|
+
text-align: left;
|
|
2196
|
+
color: var(--text-muted);
|
|
2197
|
+
padding: 4px 8px;
|
|
2198
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
2199
|
+
text-transform: uppercase;
|
|
2200
|
+
letter-spacing: 0.04em;
|
|
2201
|
+
font-size: 10px;
|
|
2202
|
+
font-weight: 600;
|
|
2203
|
+
}
|
|
2204
|
+
.twm-bp-log td {
|
|
2205
|
+
padding: 2px 8px;
|
|
2206
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
2207
|
+
vertical-align: top;
|
|
2208
|
+
}
|
|
2209
|
+
.twm-bp-log__tick { color: var(--text-muted); white-space: nowrap; }
|
|
2210
|
+
.twm-bp-log__level {
|
|
2211
|
+
color: var(--text-secondary);
|
|
2212
|
+
text-transform: lowercase;
|
|
2213
|
+
white-space: nowrap;
|
|
2214
|
+
}
|
|
2215
|
+
.twm-bp-log__row--warn .twm-bp-log__level,
|
|
2216
|
+
.twm-bp-log__row--warn .twm-bp-log__msg { color: var(--color-warning); }
|
|
2217
|
+
.twm-bp-log__row--error .twm-bp-log__level,
|
|
2218
|
+
.twm-bp-log__row--error .twm-bp-log__msg { color: var(--color-danger); }
|
|
2219
|
+
.twm-bp-log__msg { color: var(--text-body); white-space: pre-wrap; word-break: break-word; }
|
|
2220
|
+
/* Context menu — override main_new.css's .twm-context-menu to the tighter,
|
|
2221
|
+
* grid-style look (icon | label) with a flat surface-1 background and
|
|
2222
|
+
* square corners. All callers (sidebars, tile chrome, desktop bar, …)
|
|
2223
|
+
* inherit this look — one implementation, one style. */
|
|
2224
|
+
.twm-context-menu {
|
|
2225
|
+
background: var(--surface-1);
|
|
2226
|
+
border: 1px solid var(--border-default);
|
|
2227
|
+
border-radius: 0;
|
|
2228
|
+
box-shadow: var(--shadow-md);
|
|
2229
|
+
padding: 4px 0;
|
|
2230
|
+
min-width: 200px;
|
|
2231
|
+
backdrop-filter: none;
|
|
2232
|
+
}
|
|
2233
|
+
.twm-context-menu-item {
|
|
2234
|
+
display: grid;
|
|
2235
|
+
grid-template-columns: 22px 1fr;
|
|
2236
|
+
align-items: center;
|
|
2237
|
+
gap: 8px;
|
|
2238
|
+
padding: 4px 10px;
|
|
2239
|
+
font-family: var(--font-family-sans);
|
|
2240
|
+
font-size: var(--font-size-sm);
|
|
2241
|
+
color: var(--text-primary);
|
|
2242
|
+
}
|
|
2243
|
+
.twm-context-menu-item.twm-delete-node {
|
|
2244
|
+
color: var(--color-danger);
|
|
2245
|
+
}
|
|
2246
|
+
.twm-context-menu__separator {
|
|
2247
|
+
height: 1px;
|
|
2248
|
+
margin: 4px 0;
|
|
2249
|
+
background: var(--border-subtle);
|
|
2250
|
+
}
|
|
2251
|
+
/* Window-placeholder body — shown in the leaf while its content is in
|
|
2252
|
+
* a managed window. */
|
|
2253
|
+
.twm-window-placeholder {
|
|
2254
|
+
height: 100%;
|
|
2255
|
+
display: flex;
|
|
2256
|
+
flex-direction: column;
|
|
2257
|
+
align-items: center;
|
|
2258
|
+
justify-content: center;
|
|
2259
|
+
gap: 8px;
|
|
2260
|
+
padding: 24px;
|
|
2261
|
+
color: var(--text-muted);
|
|
2262
|
+
text-align: center;
|
|
2263
|
+
}
|
|
2264
|
+
.twm-window-placeholder__icon {
|
|
2265
|
+
font-size: 32px;
|
|
2266
|
+
opacity: 0.5;
|
|
2267
|
+
}
|
|
2268
|
+
.twm-window-placeholder__title {
|
|
2269
|
+
color: var(--text-primary);
|
|
2270
|
+
font-weight: var(--font-weight-semibold);
|
|
2271
|
+
}
|
|
2272
|
+
.twm-window-placeholder__hint {
|
|
2273
|
+
font-size: var(--font-size-xs);
|
|
2274
|
+
}
|
|
2275
|
+
.twm-window-placeholder__btn {
|
|
2276
|
+
margin-top: 8px;
|
|
2277
|
+
padding: 4px 12px;
|
|
2278
|
+
background: var(--surface-2);
|
|
2279
|
+
border: 1px solid var(--border-subtle);
|
|
2280
|
+
border-radius: 0;
|
|
2281
|
+
color: var(--text-primary);
|
|
2282
|
+
cursor: pointer;
|
|
2283
|
+
}
|
|
2284
|
+
.twm-window-placeholder__btn:hover {
|
|
2285
|
+
background: var(--state-selected);
|
|
2286
|
+
color: var(--text-bright);
|
|
2287
|
+
border-color: var(--accent-bright);
|
|
2288
|
+
}
|
|
2289
|
+
/* Anchor bar-center next to bar-left (instead of centred). bar-right
|
|
2290
|
+
* gets pushed against the right edge with margin-left:auto so the
|
|
2291
|
+
* existing justify-content: space-between still reads correctly. */
|
|
2292
|
+
.twm-global-top-bar .twm-bar-left { flex: 0 0 auto; }
|
|
2293
|
+
.twm-global-top-bar .twm-bar-right { flex: 0 0 auto; margin-left: auto; }
|
|
2294
|
+
/* Top-nav buttons in .twm-bar-center — archaic console look: full-height,
|
|
2295
|
+
* monospace label, square, dark active-bg with white text. Each
|
|
2296
|
+
* button is only as wide as its content; the strip takes only as
|
|
2297
|
+
* much horizontal space as required. */
|
|
2298
|
+
.twm-global-top-bar .twm-bar-center.twm-top-nav {
|
|
2299
|
+
display: flex;
|
|
2300
|
+
align-items: stretch;
|
|
2301
|
+
align-self: stretch;
|
|
2302
|
+
gap: 0;
|
|
2303
|
+
padding: 0;
|
|
2304
|
+
flex: 0 0 auto;
|
|
2305
|
+
height: 100%;
|
|
2306
|
+
min-width: 0;
|
|
2307
|
+
}
|
|
2308
|
+
.twm-top-nav__btn {
|
|
2309
|
+
display: inline-flex;
|
|
2310
|
+
align-items: center;
|
|
2311
|
+
justify-content: center;
|
|
2312
|
+
gap: 6px;
|
|
2313
|
+
flex: 0 0 auto;
|
|
2314
|
+
min-width: 0;
|
|
2315
|
+
height: 100%;
|
|
2316
|
+
padding: 0 14px;
|
|
2317
|
+
background: transparent;
|
|
2318
|
+
border: none;
|
|
2319
|
+
border-right: 1px solid var(--surface-0);
|
|
2320
|
+
border-radius: 0;
|
|
2321
|
+
color: var(--text-muted);
|
|
2322
|
+
font-family: var(--font-family-mono);
|
|
2323
|
+
font-size: 11px;
|
|
2324
|
+
font-weight: var(--font-weight-medium);
|
|
2325
|
+
text-transform: uppercase;
|
|
2326
|
+
letter-spacing: 0.08em;
|
|
2327
|
+
cursor: pointer;
|
|
2328
|
+
transition: color 80ms ease, background 80ms ease;
|
|
2329
|
+
}
|
|
2330
|
+
.twm-top-nav__btn:last-child { border-right: none; }
|
|
2331
|
+
.twm-top-nav__btn:hover {
|
|
2332
|
+
background: rgba(255, 255, 255, 0.04);
|
|
2333
|
+
color: var(--text-primary);
|
|
2334
|
+
}
|
|
2335
|
+
.twm-top-nav__btn--on {
|
|
2336
|
+
color: var(--text-bright);
|
|
2337
|
+
background: var(--surface-well);
|
|
2338
|
+
}
|
|
2339
|
+
.twm-top-nav__btn--on:hover {
|
|
2340
|
+
background: #000;
|
|
2341
|
+
color: var(--text-bright);
|
|
2342
|
+
}
|
|
2343
|
+
.twm-top-nav__icon {
|
|
2344
|
+
font-size: 16px;
|
|
2345
|
+
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;
|
|
2346
|
+
}
|
|
2347
|
+
.twm-top-nav__label {
|
|
2348
|
+
/* hidden on narrow viewports; icon alone is enough */
|
|
2349
|
+
}
|