tablewalk 0.0.1
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 +553 -0
- package/dist/adapters/adapter.js +372 -0
- package/dist/adapters/connect.js +33 -0
- package/dist/adapters/mysql.js +951 -0
- package/dist/adapters/postgres.js +1000 -0
- package/dist/adapters/sqlite.js +781 -0
- package/dist/client/agent.js +262 -0
- package/dist/client/app.js +973 -0
- package/dist/client/arrange.js +254 -0
- package/dist/client/ask.js +133 -0
- package/dist/client/breakdown.js +317 -0
- package/dist/client/clauses.js +390 -0
- package/dist/client/columns.js +98 -0
- package/dist/client/complete.js +437 -0
- package/dist/client/compose.js +166 -0
- package/dist/client/composer.css +495 -0
- package/dist/client/composer.js +1972 -0
- package/dist/client/connections.js +234 -0
- package/dist/client/connmanager.js +962 -0
- package/dist/client/connurl.js +188 -0
- package/dist/client/core.js +893 -0
- package/dist/client/deeplink.js +270 -0
- package/dist/client/delete.js +144 -0
- package/dist/client/diagram.js +885 -0
- package/dist/client/dropdown.js +279 -0
- package/dist/client/export.js +456 -0
- package/dist/client/features.css +524 -0
- package/dist/client/findvalue.js +169 -0
- package/dist/client/grid.js +205 -0
- package/dist/client/handoff.js +153 -0
- package/dist/client/help.css +145 -0
- package/dist/client/help.js +881 -0
- package/dist/client/history.js +222 -0
- package/dist/client/index.html +116 -0
- package/dist/client/insert.js +151 -0
- package/dist/client/menu.js +160 -0
- package/dist/client/nested.js +255 -0
- package/dist/client/page.css +713 -0
- package/dist/client/page.js +1345 -0
- package/dist/client/pagebuilder.js +1222 -0
- package/dist/client/pagemarks.js +95 -0
- package/dist/client/palette.js +374 -0
- package/dist/client/peek.js +254 -0
- package/dist/client/picker.js +139 -0
- package/dist/client/pins.js +140 -0
- package/dist/client/prompt.js +129 -0
- package/dist/client/record.js +707 -0
- package/dist/client/schemaexport.js +242 -0
- package/dist/client/schematext.js +125 -0
- package/dist/client/shape.js +178 -0
- package/dist/client/shapecheck.js +129 -0
- package/dist/client/skeleton.js +139 -0
- package/dist/client/sql.css +126 -0
- package/dist/client/sql.js +398 -0
- package/dist/client/sqlcomplete.js +163 -0
- package/dist/client/sqlsaved.js +107 -0
- package/dist/client/style.css +2711 -0
- package/dist/client/summary.js +259 -0
- package/dist/client/table.js +1035 -0
- package/dist/client/template.js +539 -0
- package/dist/client/theme.js +74 -0
- package/dist/client/tour.js +324 -0
- package/dist/client/undo.js +105 -0
- package/dist/client/url.js +166 -0
- package/dist/client/value.js +223 -0
- package/dist/client/views.js +215 -0
- package/dist/client/virtual.js +176 -0
- package/dist/client/welcome.js +170 -0
- package/dist/client/write.js +414 -0
- package/dist/server/changeimpact.js +195 -0
- package/dist/server/connections.js +615 -0
- package/dist/server/constraints.js +62 -0
- package/dist/server/credentials.js +230 -0
- package/dist/server/fixture.js +199 -0
- package/dist/server/graph.js +194 -0
- package/dist/server/impact.js +48 -0
- package/dist/server/index.js +2204 -0
- package/dist/server/journal.js +173 -0
- package/dist/server/layouts.js +128 -0
- package/dist/server/mcp.js +2840 -0
- package/dist/server/shapeonly.js +91 -0
- package/dist/shared/breakdown.js +231 -0
- package/dist/shared/breakdowntext.js +257 -0
- package/dist/shared/diff.js +130 -0
- package/dist/shared/like.js +29 -0
- package/dist/shared/lint.js +149 -0
- package/dist/shared/order.js +133 -0
- package/dist/shared/page.js +932 -0
- package/dist/shared/query.js +831 -0
- package/dist/shared/recordview.js +343 -0
- package/dist/shared/schema.js +377 -0
- package/dist/shared/sqlsaved.js +67 -0
- package/dist/shared/view.js +981 -0
- package/dist/shared/viewtext.js +273 -0
- package/dist/shared/vocabulary.js +164 -0
- package/package.json +57 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
/* The view composer.
|
|
2
|
+
|
|
3
|
+
Every colour here is a token from style.css — nothing names a colour
|
|
4
|
+
directly, so the composer follows the light/dark switch without knowing
|
|
5
|
+
there is one. Shapes are borrowed too: `.panel`, `.tab`, `.ac`, `.dd` and
|
|
6
|
+
the results grid all come from the main sheet and are only positioned
|
|
7
|
+
here, so a view looks like the rest of the tool rather than like a second
|
|
8
|
+
application bolted to the side of it. */
|
|
9
|
+
|
|
10
|
+
.composer {
|
|
11
|
+
position: fixed;
|
|
12
|
+
top: 0; /* set from the top bar's height on open — see positionRoot() */
|
|
13
|
+
left: 0;
|
|
14
|
+
right: 0;
|
|
15
|
+
bottom: 0;
|
|
16
|
+
z-index: 40;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
background: var(--bg);
|
|
20
|
+
color: var(--text);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* The page behind is inert but still visible above the composer; stopping it
|
|
24
|
+
from scrolling keeps the two layers from arguing about the wheel. */
|
|
25
|
+
body.composing { overflow: hidden; }
|
|
26
|
+
|
|
27
|
+
/* ---------- the composer's own bar ---------- */
|
|
28
|
+
|
|
29
|
+
.composer-bar {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: 10px;
|
|
33
|
+
flex: none;
|
|
34
|
+
padding: 8px 14px;
|
|
35
|
+
background: var(--surface);
|
|
36
|
+
border-bottom: 1px solid var(--border);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.composer-eyebrow {
|
|
40
|
+
font-size: 10px;
|
|
41
|
+
text-transform: uppercase;
|
|
42
|
+
letter-spacing: 0.08em;
|
|
43
|
+
color: var(--text-faint);
|
|
44
|
+
flex: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.composer-name {
|
|
48
|
+
flex: 1;
|
|
49
|
+
min-width: 0;
|
|
50
|
+
max-width: 26rem;
|
|
51
|
+
padding: 6px 9px;
|
|
52
|
+
font: 13px var(--sans);
|
|
53
|
+
color: var(--text);
|
|
54
|
+
background: var(--bg);
|
|
55
|
+
border: 1px solid var(--border-strong);
|
|
56
|
+
border-radius: var(--radius);
|
|
57
|
+
}
|
|
58
|
+
.composer-name:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }
|
|
59
|
+
|
|
60
|
+
.composer-on {
|
|
61
|
+
color: var(--text-dim);
|
|
62
|
+
font: 12px var(--mono);
|
|
63
|
+
flex: none;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.composer-bar-actions { display: flex; gap: 6px; margin-left: auto; flex: none; }
|
|
67
|
+
|
|
68
|
+
.composer-close { font-size: 15px; line-height: 1; padding: 5px 10px; }
|
|
69
|
+
|
|
70
|
+
/* ---------- two columns ---------- */
|
|
71
|
+
|
|
72
|
+
.composer-body { display: flex; flex: 1; min-height: 0; }
|
|
73
|
+
|
|
74
|
+
/* The sidebar itself does not scroll; its sections do.
|
|
75
|
+
|
|
76
|
+
With one scroller for the whole column, the relationship list — which is
|
|
77
|
+
long, and the tallest thing here — pushes everything under it off the
|
|
78
|
+
bottom of the window. Saved views were reachable only by scrolling past
|
|
79
|
+
thirty relationships, which is to say they were not reachable. Giving the
|
|
80
|
+
long section its own scroller pins the short ones in place. */
|
|
81
|
+
.composer-side {
|
|
82
|
+
width: 336px;
|
|
83
|
+
flex: none;
|
|
84
|
+
border-right: 1px solid var(--border);
|
|
85
|
+
background: var(--surface);
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
padding: 10px;
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
gap: 10px;
|
|
91
|
+
min-height: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.composer-panel { display: flex; flex-direction: column; min-height: 0; }
|
|
95
|
+
.composer-panel > h3 { flex: none; }
|
|
96
|
+
.composer-panel > .composer-rel,
|
|
97
|
+
.composer-panel > .composer-saved { overflow-y: auto; min-height: 0; }
|
|
98
|
+
|
|
99
|
+
/* Saved views take what they need up to a third of the column; the
|
|
100
|
+
relationship list takes the rest. */
|
|
101
|
+
.composer-side > .composer-panel:nth-child(2) > .composer-saved { max-height: 32vh; }
|
|
102
|
+
.composer-related { flex: 1; }
|
|
103
|
+
|
|
104
|
+
.composer-main {
|
|
105
|
+
flex: 1;
|
|
106
|
+
min-width: 0;
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-direction: column;
|
|
109
|
+
overflow: hidden;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.composer-pad { padding: 8px 10px; }
|
|
113
|
+
|
|
114
|
+
/* ---------- related tables ----------
|
|
115
|
+
|
|
116
|
+
The phrase leads and the mechanism follows it in mono, one size down: you
|
|
117
|
+
read "each invoice has one customer" and only look at `via customer_id` if
|
|
118
|
+
you need to know how. Reversing that order is what makes join builders feel
|
|
119
|
+
like paperwork. */
|
|
120
|
+
|
|
121
|
+
.composer-rel { list-style: none; margin: 0; padding: 4px 0 6px; }
|
|
122
|
+
|
|
123
|
+
.composer-rel-head {
|
|
124
|
+
padding: 8px 10px 3px;
|
|
125
|
+
font: 600 10px var(--sans);
|
|
126
|
+
text-transform: uppercase;
|
|
127
|
+
letter-spacing: 0.07em;
|
|
128
|
+
color: var(--text-faint);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.composer-rel-item {
|
|
132
|
+
display: grid;
|
|
133
|
+
grid-template-columns: 1fr auto;
|
|
134
|
+
align-items: baseline;
|
|
135
|
+
gap: 2px 8px;
|
|
136
|
+
padding: 5px 10px;
|
|
137
|
+
border-left: 2px solid transparent;
|
|
138
|
+
}
|
|
139
|
+
.composer-rel-item:hover { background: var(--surface-2); }
|
|
140
|
+
.composer-rel-item.open { border-left-color: var(--accent); background: var(--accent-soft); }
|
|
141
|
+
|
|
142
|
+
.composer-phrase { font: 12.5px/1.35 var(--sans); color: var(--text); }
|
|
143
|
+
.composer-rel-item.many .composer-phrase { color: var(--text-dim); }
|
|
144
|
+
|
|
145
|
+
.composer-via {
|
|
146
|
+
grid-column: 1;
|
|
147
|
+
font: 11px var(--mono);
|
|
148
|
+
color: var(--text-faint);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.composer-add {
|
|
152
|
+
grid-row: 1 / span 2;
|
|
153
|
+
grid-column: 2;
|
|
154
|
+
align-self: center;
|
|
155
|
+
background: none;
|
|
156
|
+
border: 1px solid var(--border-strong);
|
|
157
|
+
border-radius: 999px;
|
|
158
|
+
color: var(--text-dim);
|
|
159
|
+
padding: 2px 10px;
|
|
160
|
+
font: 11.5px var(--sans);
|
|
161
|
+
cursor: pointer;
|
|
162
|
+
}
|
|
163
|
+
.composer-add:hover { border-color: var(--accent); color: var(--accent); }
|
|
164
|
+
.composer-add.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
|
|
165
|
+
|
|
166
|
+
.composer-cap {
|
|
167
|
+
grid-row: 1 / span 2;
|
|
168
|
+
grid-column: 2;
|
|
169
|
+
align-self: center;
|
|
170
|
+
font: 11px var(--mono);
|
|
171
|
+
color: var(--text-faint);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* A table reachable several ways is stated once, with its routes beneath.
|
|
175
|
+
Three sentences differing only in a trailing column name is a list you have
|
|
176
|
+
to read rather than scan. */
|
|
177
|
+
.composer-rel-group {
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: baseline;
|
|
180
|
+
gap: 8px;
|
|
181
|
+
padding: 8px 10px 2px;
|
|
182
|
+
}
|
|
183
|
+
.composer-rel-group .composer-phrase { font-size: 12.5px; }
|
|
184
|
+
.composer-ways {
|
|
185
|
+
margin-left: auto;
|
|
186
|
+
flex: none;
|
|
187
|
+
font: 10.5px var(--mono);
|
|
188
|
+
color: var(--text-faint);
|
|
189
|
+
}
|
|
190
|
+
.composer-rel-item.in-group { padding-top: 2px; padding-bottom: 2px; }
|
|
191
|
+
.composer-rel-item.in-group .composer-via { grid-row: 1; align-self: center; }
|
|
192
|
+
|
|
193
|
+
/* The many side: two real options and a warning, rather than a disabled
|
|
194
|
+
button and no explanation.
|
|
195
|
+
|
|
196
|
+
Its controls sit on their own row rather than beside the sentence. In a
|
|
197
|
+
`1fr auto` row the `auto` column sizes to three stacked controls, which on
|
|
198
|
+
a 336px sidebar left the sentence a column about one word wide — "each /
|
|
199
|
+
customer / has many / invoices", read downwards. A sentence and a control
|
|
200
|
+
stack cannot share a row this narrow, so they do not. */
|
|
201
|
+
.composer-rel-item.many { grid-template-columns: 1fr; }
|
|
202
|
+
.composer-many-actions {
|
|
203
|
+
grid-row: auto;
|
|
204
|
+
grid-column: 1;
|
|
205
|
+
display: flex;
|
|
206
|
+
align-items: center;
|
|
207
|
+
gap: 6px;
|
|
208
|
+
flex-wrap: wrap;
|
|
209
|
+
justify-content: flex-start;
|
|
210
|
+
margin-top: 4px;
|
|
211
|
+
}
|
|
212
|
+
.composer-menu { display: inline-block; min-width: 9rem; }
|
|
213
|
+
.composer-menu .dd-button { padding: 2px 8px; font-size: 11.5px; }
|
|
214
|
+
/* Positioning is handled in the viewport by dropdown.js, so nothing here
|
|
215
|
+
needs to fight the sidebar's clip. */
|
|
216
|
+
.composer-menu .dd-list { min-width: 13rem; }
|
|
217
|
+
|
|
218
|
+
/* ---------- saved views ---------- */
|
|
219
|
+
|
|
220
|
+
.composer-saved { list-style: none; margin: 0; padding: 4px 0 6px; }
|
|
221
|
+
.composer-saved-item { display: flex; align-items: stretch; }
|
|
222
|
+
.composer-saved-item:hover { background: var(--surface-2); }
|
|
223
|
+
.composer-saved-item.active { background: var(--accent-soft); }
|
|
224
|
+
|
|
225
|
+
.composer-pin {
|
|
226
|
+
flex: none;
|
|
227
|
+
align-self: center;
|
|
228
|
+
background: none;
|
|
229
|
+
border: none;
|
|
230
|
+
border-radius: 0;
|
|
231
|
+
color: var(--text-faint);
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
padding: 6px 2px 6px 8px;
|
|
234
|
+
font-size: 12px;
|
|
235
|
+
line-height: 1;
|
|
236
|
+
}
|
|
237
|
+
.composer-pin:hover { color: var(--text-dim); }
|
|
238
|
+
.composer-pin.on { color: var(--accent); }
|
|
239
|
+
|
|
240
|
+
.composer-saved-open {
|
|
241
|
+
flex: 1;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
padding-left: 4px;
|
|
244
|
+
display: flex;
|
|
245
|
+
flex-direction: column;
|
|
246
|
+
gap: 1px;
|
|
247
|
+
background: none;
|
|
248
|
+
border: none;
|
|
249
|
+
border-radius: 0;
|
|
250
|
+
text-align: left;
|
|
251
|
+
padding: 6px 10px;
|
|
252
|
+
cursor: pointer;
|
|
253
|
+
color: var(--text);
|
|
254
|
+
}
|
|
255
|
+
.composer-saved-open[disabled] { color: var(--text-faint); cursor: default; }
|
|
256
|
+
.composer-saved-name {
|
|
257
|
+
font: 12.5px var(--sans);
|
|
258
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
259
|
+
}
|
|
260
|
+
.composer-saved-item.active .composer-saved-name { color: var(--accent); font-weight: 600; }
|
|
261
|
+
.composer-saved-base { font: 11px var(--mono); color: var(--text-dim); }
|
|
262
|
+
|
|
263
|
+
.composer-saved-tag {
|
|
264
|
+
align-self: center;
|
|
265
|
+
flex: none;
|
|
266
|
+
margin-right: 8px;
|
|
267
|
+
font-size: 9.5px;
|
|
268
|
+
text-transform: uppercase;
|
|
269
|
+
letter-spacing: 0.05em;
|
|
270
|
+
color: var(--text-dim);
|
|
271
|
+
background: var(--surface-2);
|
|
272
|
+
border-radius: 3px;
|
|
273
|
+
padding: 1px 5px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ---------- fields ---------- */
|
|
277
|
+
|
|
278
|
+
.composer-fields {
|
|
279
|
+
margin: 10px 14px 0;
|
|
280
|
+
max-height: 38%;
|
|
281
|
+
overflow-y: auto;
|
|
282
|
+
flex: none;
|
|
283
|
+
}
|
|
284
|
+
.composer-fields > h3 { position: sticky; top: 0; z-index: 1; display: flex; gap: 8px; align-items: baseline; }
|
|
285
|
+
.composer-count { margin-left: auto; font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 11px; }
|
|
286
|
+
|
|
287
|
+
.composer-empty {
|
|
288
|
+
margin: 0;
|
|
289
|
+
padding: 10px 12px 12px;
|
|
290
|
+
font-size: 12.5px;
|
|
291
|
+
line-height: 1.5;
|
|
292
|
+
color: var(--text-dim);
|
|
293
|
+
border-bottom: 1px solid var(--border);
|
|
294
|
+
}
|
|
295
|
+
.composer-empty strong { color: var(--text); font-weight: 600; }
|
|
296
|
+
|
|
297
|
+
.composer-group { border-bottom: 1px solid var(--border); }
|
|
298
|
+
.composer-group:last-child { border-bottom: none; }
|
|
299
|
+
/* A moment's emphasis on the group that just arrived, then nothing. A badge
|
|
300
|
+
that stays becomes furniture and stops meaning "new". */
|
|
301
|
+
.composer-group.fresh { background: var(--accent-soft); }
|
|
302
|
+
.composer-group-new {
|
|
303
|
+
margin-left: auto;
|
|
304
|
+
font-size: 9.5px;
|
|
305
|
+
text-transform: uppercase;
|
|
306
|
+
letter-spacing: 0.06em;
|
|
307
|
+
color: var(--accent);
|
|
308
|
+
}
|
|
309
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
310
|
+
.composer-group { transition: background 400ms ease; }
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.composer-group-head {
|
|
314
|
+
display: flex;
|
|
315
|
+
align-items: baseline;
|
|
316
|
+
gap: 8px;
|
|
317
|
+
padding: 6px 12px 2px;
|
|
318
|
+
}
|
|
319
|
+
.composer-group-name { font: 600 12px var(--mono); color: var(--text); }
|
|
320
|
+
.composer-group-path { font: 11px var(--mono); color: var(--text-faint); }
|
|
321
|
+
|
|
322
|
+
/* A wrapping grid rather than a list: a table with twenty-seven columns is
|
|
323
|
+
the case that matters, and one per line makes it a scroll instead of a
|
|
324
|
+
glance. */
|
|
325
|
+
.composer-checks {
|
|
326
|
+
display: flex;
|
|
327
|
+
flex-wrap: wrap;
|
|
328
|
+
gap: 2px 4px;
|
|
329
|
+
padding: 2px 10px 8px;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.composer-check {
|
|
333
|
+
display: inline-flex;
|
|
334
|
+
align-items: baseline;
|
|
335
|
+
gap: 5px;
|
|
336
|
+
padding: 3px 8px;
|
|
337
|
+
border: 1px solid var(--border);
|
|
338
|
+
border-radius: 999px;
|
|
339
|
+
cursor: pointer;
|
|
340
|
+
font: 12px var(--mono);
|
|
341
|
+
color: var(--text-dim);
|
|
342
|
+
max-width: 100%;
|
|
343
|
+
}
|
|
344
|
+
.composer-check:hover { border-color: var(--border-strong); color: var(--text); }
|
|
345
|
+
.composer-check.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
|
|
346
|
+
.composer-check input { margin: 0; accent-color: var(--accent); }
|
|
347
|
+
.composer-field-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
348
|
+
.composer-field-type { font-size: 10.5px; color: var(--text-faint); }
|
|
349
|
+
.composer-mark { font-size: 10px; }
|
|
350
|
+
.composer-mark.pk { color: var(--key); }
|
|
351
|
+
.composer-mark.fk { color: var(--accent); }
|
|
352
|
+
|
|
353
|
+
/* ---------- filter ---------- */
|
|
354
|
+
|
|
355
|
+
.composer-filter { flex: none; padding: 10px 14px 0; }
|
|
356
|
+
|
|
357
|
+
.composer-filter-row { display: flex; align-items: center; gap: 8px; }
|
|
358
|
+
|
|
359
|
+
.composer-filter-label {
|
|
360
|
+
font: 11px var(--mono);
|
|
361
|
+
color: var(--text-faint);
|
|
362
|
+
text-transform: uppercase;
|
|
363
|
+
letter-spacing: 0.06em;
|
|
364
|
+
flex: none;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.composer-query-wrap { position: relative; flex: 1; min-width: 0; display: block; }
|
|
368
|
+
|
|
369
|
+
.composer-query {
|
|
370
|
+
width: 100%;
|
|
371
|
+
padding: 7px 10px;
|
|
372
|
+
font: 13px/1.4 var(--mono);
|
|
373
|
+
color: var(--text);
|
|
374
|
+
background: var(--bg);
|
|
375
|
+
border: 1px solid var(--border-strong);
|
|
376
|
+
border-radius: var(--radius);
|
|
377
|
+
}
|
|
378
|
+
.composer-query:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }
|
|
379
|
+
|
|
380
|
+
.composer-limit {
|
|
381
|
+
width: 5rem;
|
|
382
|
+
padding: 7px 8px;
|
|
383
|
+
font: 13px var(--mono);
|
|
384
|
+
color: var(--text);
|
|
385
|
+
background: var(--bg);
|
|
386
|
+
border: 1px solid var(--border-strong);
|
|
387
|
+
border-radius: var(--radius);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* The suggestion list is `.ac` from style.css; only its anchor changes. */
|
|
391
|
+
.composer-ac { top: calc(100% + 4px); left: 0; }
|
|
392
|
+
|
|
393
|
+
.composer-hint { margin: 6px 0 0; font-size: 11.5px; color: var(--text-faint); }
|
|
394
|
+
|
|
395
|
+
/* What the result *is*, in words, immediately above the grid that shows it.
|
|
396
|
+
The panels above describe ingredients; this is the only line that describes
|
|
397
|
+
the answer, which is the thing people cannot otherwise assemble in their
|
|
398
|
+
heads. */
|
|
399
|
+
.composer-summary {
|
|
400
|
+
flex: none;
|
|
401
|
+
margin: 10px 0 0;
|
|
402
|
+
padding: 8px 14px;
|
|
403
|
+
font-size: 12.5px;
|
|
404
|
+
line-height: 1.5;
|
|
405
|
+
color: var(--text-dim);
|
|
406
|
+
background: var(--surface-2);
|
|
407
|
+
border-top: 1px solid var(--border);
|
|
408
|
+
border-bottom: 1px solid var(--border);
|
|
409
|
+
}
|
|
410
|
+
.composer-summary strong { color: var(--text); font-weight: 600; }
|
|
411
|
+
|
|
412
|
+
/* ---------- the view, written down ---------- */
|
|
413
|
+
|
|
414
|
+
.composer-textpanel {
|
|
415
|
+
flex: none;
|
|
416
|
+
margin: 10px 14px 0;
|
|
417
|
+
display: flex;
|
|
418
|
+
flex-direction: column;
|
|
419
|
+
gap: 6px;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.composer-text {
|
|
423
|
+
width: 100%;
|
|
424
|
+
resize: vertical;
|
|
425
|
+
padding: 10px 12px;
|
|
426
|
+
font: 12.5px/1.6 var(--mono);
|
|
427
|
+
color: var(--text);
|
|
428
|
+
background: var(--surface);
|
|
429
|
+
border: 1px solid var(--border-strong);
|
|
430
|
+
border-radius: 8px;
|
|
431
|
+
tab-size: 2;
|
|
432
|
+
}
|
|
433
|
+
.composer-text:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent; }
|
|
434
|
+
|
|
435
|
+
.composer-text-actions { display: flex; align-items: center; gap: 10px; }
|
|
436
|
+
.composer-text-actions .composer-hint { margin: 0; flex: 1; min-width: 0; }
|
|
437
|
+
.composer-text-apply { padding: 4px 12px; font-size: 12px; }
|
|
438
|
+
.composer-text-apply[disabled] { opacity: 0.45; cursor: default; }
|
|
439
|
+
|
|
440
|
+
/* ---------- SQL ---------- */
|
|
441
|
+
|
|
442
|
+
.composer-explain {
|
|
443
|
+
flex: none;
|
|
444
|
+
margin: 10px 14px 0;
|
|
445
|
+
max-height: 12rem;
|
|
446
|
+
overflow: auto;
|
|
447
|
+
border: 1px solid var(--border);
|
|
448
|
+
border-radius: 8px;
|
|
449
|
+
background: var(--surface-2);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/* ---------- preview ---------- */
|
|
453
|
+
|
|
454
|
+
.composer-preview { flex: 1; min-height: 0; overflow: auto; }
|
|
455
|
+
.composer-preview .result-head { padding-top: 12px; }
|
|
456
|
+
.composer-errors { border-bottom: none; margin: 10px 14px; border-radius: 8px; }
|
|
457
|
+
|
|
458
|
+
/* Headers in a view carry two names — the table and the column — because a
|
|
459
|
+
view of thirty fields drawn from five tables is unreadable when every third
|
|
460
|
+
one is called "name". */
|
|
461
|
+
.composer-th-table { color: var(--text-faint); font-weight: 400; text-transform: none; letter-spacing: 0; }
|
|
462
|
+
/* A summary is a different kind of column — computed from many rows rather
|
|
463
|
+
than read from one — and is coloured like a key so the eye separates it
|
|
464
|
+
from the fields beside it. */
|
|
465
|
+
.composer-preview th.is-summary { cursor: default; color: var(--key); }
|
|
466
|
+
.composer-preview th.is-summary:hover { color: var(--key); }
|
|
467
|
+
.composer-preview th.is-summary .composer-th-table { color: var(--key); opacity: 0.75; }
|
|
468
|
+
.composer-th-sort { color: var(--accent); }
|
|
469
|
+
.composer-preview th.sorted-asc, .composer-preview th.sorted-desc { color: var(--accent); }
|
|
470
|
+
/* Nothing to click through to: a view row is a join of several rows and has
|
|
471
|
+
no single identity to open. Saying so with the cursor is cheaper than
|
|
472
|
+
letting someone find out. */
|
|
473
|
+
.composer-preview tbody tr { cursor: default; }
|
|
474
|
+
.composer-preview tbody tr:hover { background: var(--surface-2); }
|
|
475
|
+
|
|
476
|
+
/* ---------- narrow windows ---------- */
|
|
477
|
+
|
|
478
|
+
@media (max-width: 860px) {
|
|
479
|
+
.composer-body { flex-direction: column; }
|
|
480
|
+
.composer-side { width: auto; border-right: none; border-bottom: 1px solid var(--border); max-height: 40%; }
|
|
481
|
+
.composer-fields { max-height: none; }
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/* Renaming a column, on the header where the ambiguity shows. Hidden until
|
|
485
|
+
the header is hovered: it is an occasional action and a pencil on every
|
|
486
|
+
column of a thirty-column view is noise. */
|
|
487
|
+
.composer-th-rename {
|
|
488
|
+
background: none; border: none; border-radius: 3px;
|
|
489
|
+
color: var(--text-faint); cursor: pointer;
|
|
490
|
+
padding: 0 4px; margin-left: 6px; font-size: 10px; line-height: 1;
|
|
491
|
+
opacity: 0; transition: opacity 120ms ease;
|
|
492
|
+
}
|
|
493
|
+
th:hover .composer-th-rename,
|
|
494
|
+
.composer-th-rename:focus-visible { opacity: 1; }
|
|
495
|
+
.composer-th-rename:hover { color: var(--accent); }
|