pgkiln 0.46.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/db/migrations/108_sql_history.sql +25 -0
  2. package/db/migrations/109_builder_pins.sql +19 -0
  3. package/db/migrations/110_sql_charts.sql +23 -0
  4. package/db/migrations/111_sql_diagram.sql +24 -0
  5. package/db/migrations/113_rest_objects.sql +49 -0
  6. package/docs/apex-feature-parity.md +28 -2
  7. package/docs/guide/03-builder.md +178 -21
  8. package/docs/guide/08-security.md +19 -1
  9. package/docs/guide/10-tutorial.md +1 -1
  10. package/docs/guide/12-development.md +46 -2
  11. package/docs/guide/13-rest-api.md +74 -9
  12. package/docs/guide/16-files.md +2 -2
  13. package/package.json +1 -1
  14. package/public/builder.css +22 -0
  15. package/public/data-modeler.css +56 -0
  16. package/public/data-modeler.js +367 -0
  17. package/public/palette.js +133 -0
  18. package/public/worksheet.css +293 -0
  19. package/public/worksheet.js +669 -0
  20. package/src/app.ts +4 -0
  21. package/src/builder/ai-builder.ts +3 -3
  22. package/src/builder/autorest.ts +362 -0
  23. package/src/builder/data-modeler.ts +382 -0
  24. package/src/builder/db-users.ts +472 -0
  25. package/src/builder/launchpad.ts +140 -0
  26. package/src/builder/object-actions.ts +167 -0
  27. package/src/builder/object-browser.ts +47 -26
  28. package/src/builder/querybuilder.ts +1 -1
  29. package/src/builder/sampledata.ts +2 -2
  30. package/src/builder/scripts.ts +3 -3
  31. package/src/builder/sql-charts.ts +203 -0
  32. package/src/builder/sql-tools.ts +49 -0
  33. package/src/builder/sql.ts +28 -81
  34. package/src/builder/table-editor.ts +219 -0
  35. package/src/builder/ui.ts +21 -15
  36. package/src/builder/unload.ts +8 -4
  37. package/src/builder/worksheet.ts +592 -0
  38. package/src/builder/workspaces.ts +5 -3
  39. package/src/cli/replace.ts +3 -1
  40. package/src/datamodel.ts +324 -0
  41. package/src/dbroles.ts +215 -0
  42. package/src/runtime/autorest.ts +336 -0
  43. package/src/runtime/rest.ts +4 -4
  44. package/src/session.ts +2 -0
  45. package/src/sqlformat.ts +208 -0
  46. package/src/sqlplan.ts +133 -0
  47. package/src/sqlscript.ts +6 -2
  48. package/src/sqlworksheet.ts +278 -0
  49. package/src/tabledef.ts +226 -0
  50. package/src/unload.ts +14 -3
@@ -0,0 +1,293 @@
1
+ /* pgkiln builder: the SQL Worksheet (sprint 91, src/builder/worksheet.ts, public/worksheet.js).
2
+ Narrow screens and pages without script: the panes stack and the page scrolls. With script on wide
3
+ screens: navigator | editor over output tabs, filling the window, with a status bar at the bottom. */
4
+ .ws-page { display: flex; flex-direction: column; padding: 8px 10px 0; background: var(--bg); }
5
+ .ws-page > .sw-tabs { margin-bottom: 6px; }
6
+ .ws { --ws-nav: 280px; --ws-out: 42vh; display: grid; grid-template-columns: minmax(0, 1fr); gap: 0; background: var(--surface);
7
+ border: 1px solid var(--border); border-radius: var(--radius); min-width: 0; }
8
+ .ws-default { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); border: 0; padding: 0; }
9
+ .ws-nav { display: flex; flex-direction: column; min-width: 0; border-bottom: 1px solid var(--border); background: var(--surface-2); }
10
+ .ws-nav-head { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 8px; border-bottom: 1px solid var(--border); }
11
+ .ws-nav-head select { min-width: 0; font-size: 13px; }
12
+ .ws-nav-search { grid-column: 1 / -1; display: flex; gap: 6px; }
13
+ .ws-nav-search input { flex: 1; min-width: 0; font-size: 13px; }
14
+ .ws-nav-body { min-height: 0; max-height: 40vh; overflow: auto; padding: 4px 0; font-size: 13px; }
15
+ .ws-nav-body[aria-busy=true] { opacity: 0.6; }
16
+ .ws-nav-empty { padding: 8px 12px; margin: 0; }
17
+ .ws-tree, .ws-cols { list-style: none; margin: 0; padding: 0; }
18
+ .ws-obj > a, .ws-obj > details > summary { display: flex; align-items: center; gap: 6px; padding: 3px 10px; color: var(--text); text-decoration: none; cursor: pointer; min-width: 0; }
19
+ .ws-obj > details > summary { list-style: none; }
20
+ .ws-obj > details > summary::-webkit-details-marker { display: none; }
21
+ .ws-obj > details > summary::before { content: '▸'; color: var(--muted); width: 0.8em; flex: none; }
22
+ .ws-obj > details[open] > summary::before { content: '▾'; }
23
+ .ws-obj > a { padding-left: calc(10px + 0.8em + 6px); }
24
+ .ws-obj > a:hover, .ws-obj > details > summary:hover { background: var(--surface-3); }
25
+ .ws-obj > a span, .ws-obj > details > summary span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
26
+ .ws-obj .icon { width: 14px; height: 14px; flex: none; color: var(--muted); }
27
+ .ws-obj .tag { margin-left: auto; font-size: 10px; }
28
+ .ws-obj > details > summary[draggable=true] { cursor: grab; }
29
+ .ws-cols li { display: flex; align-items: center; gap: 6px; padding: 1px 10px 1px 34px; min-width: 0; }
30
+ .ws-cols small { margin-left: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 50%; }
31
+ .ws-cols .icon { width: 12px; height: 12px; }
32
+ .ws-obj-actions { display: flex; flex-wrap: wrap; gap: 4px; padding: 4px 10px 8px 34px; }
33
+ .ws-obj-actions .btn { font-size: 11px; padding: 1px 6px; }
34
+
35
+ .ws-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
36
+ .ws-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--border); }
37
+ .ws-toolbar .btn { display: inline-flex; align-items: center; gap: 4px; }
38
+ .ws-toolbar .icon { width: 15px; height: 15px; }
39
+ .ws-check, .ws-runas { white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }
40
+ .ws-runas select { max-width: 16rem; font-size: 13px; }
41
+ .ws-spacer { flex: 1; }
42
+ .ws-runs-as { margin: 0; padding: 4px 8px; border-bottom: 1px solid var(--border); }
43
+ .ws-editor { min-height: 0; display: flex; flex-direction: column; }
44
+ .ws-editor textarea { width: 100%; min-height: 12rem; border: 0; border-radius: 0; }
45
+ .ws-editor > :is(textarea, .ce) { flex: 1 1 auto; }
46
+
47
+ .ws-output { display: flex; flex-direction: column; min-height: 0; border-top: 1px solid var(--border); }
48
+ .ws-tabs { display: none; gap: 0; border-bottom: 1px solid var(--border); overflow-x: auto; background: var(--surface-2); }
49
+ .js .ws-tabs { display: flex; }
50
+ .ws-tabs [role=tab] { flex: none; border: 0; border-bottom: 2px solid transparent; background: none; color: var(--muted); padding: 6px 12px; font: inherit; font-size: 13px; cursor: pointer; }
51
+ .ws-tabs [role=tab][aria-selected=true] { color: var(--text); border-bottom-color: var(--hot); }
52
+ .ws-tabs [role=tab]:focus-visible { outline: 2px solid var(--hot); outline-offset: -2px; }
53
+ .ws-pane { padding: 8px; min-height: 0; overflow: auto; }
54
+ .ws-pane-title { font-size: 14px; margin: 0 0 6px; }
55
+ .js .ws-pane-title { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
56
+ .ws-info { margin: 0 0 6px; }
57
+ .ws-grid { max-height: none; }
58
+ .ws-grid td, .ws-grid th { padding: 3px 8px; font-size: 12.5px; }
59
+ .ws-grid .ws-rownum { color: var(--muted); text-align: right; width: 1%; }
60
+ .ws-grid .null { color: var(--muted); font-style: italic; }
61
+ .ws-log { margin: 0; padding: 8px; background: var(--code-bg); border: 1px solid var(--border); border-radius: var(--radius); font-size: 12.5px; line-height: 1.45; white-space: pre; overflow: auto; }
62
+ .ws-log-sql { color: var(--muted); }
63
+ .ws-log-ok { color: #2f7d32; font-weight: 600; }
64
+ .ws-log-err { color: #c0392b; white-space: pre-wrap; }
65
+ .ws-log-warn { color: var(--warn); white-space: pre-wrap; }
66
+ .ws-notices, .ws-history { list-style: none; margin: 0; padding: 0; font-size: 13px; }
67
+ .ws-notices li, .ws-history li { display: flex; gap: 8px; align-items: baseline; padding: 4px 0; border-bottom: 1px solid var(--border); min-width: 0; }
68
+ .ws-notices .tag-warn { color: var(--warn); }
69
+ .ws-history a { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
70
+ .ws-history small { flex: none; }
71
+
72
+ .ws-status { display: flex; align-items: center; gap: 12px; padding: 4px 10px; border-top: 1px solid var(--border); background: var(--surface-2); font-size: 12.5px; min-width: 0; }
73
+ .ws-status .icon { width: 13px; height: 13px; vertical-align: -2px; }
74
+ .ws-count { color: var(--muted); white-space: nowrap; }
75
+ .ws-count.is-err { color: #c0392b; }
76
+ .ws-count.is-warn { color: var(--warn); }
77
+ .ws-last { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
78
+ .ws-time { margin-left: auto; }
79
+ .ws-split { display: none; }
80
+
81
+ @media (min-width: 900px) {
82
+ .ws { grid-template-columns: var(--ws-nav) auto minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
83
+ .ws-nav { grid-row: 1; grid-column: 1; border-bottom: 0; }
84
+ .ws-split-v { grid-row: 1; grid-column: 2; }
85
+ .ws-nav-body { max-height: none; flex: 1 1 auto; }
86
+ .ws-main { grid-row: 1; grid-column: 3; }
87
+ .ws-status { grid-column: 1 / -1; }
88
+ .ws-nav-hidden { grid-template-columns: 0 auto minmax(0, 1fr); }
89
+ .ws-nav-hidden .ws-nav { visibility: hidden; }
90
+ .js .ws-split { display: block; background: var(--border); }
91
+ .js .ws-split-v { width: 5px; cursor: col-resize; }
92
+ .js .ws-split-h { height: 5px; cursor: row-resize; flex: none; }
93
+ .ws-split:hover, .ws-split:focus-visible { background: var(--hot); outline: none; }
94
+ /* with script the worksheet fills the window and each pane scrolls on its own */
95
+ .js .ide-full .ws-page { height: 100%; min-height: 0; }
96
+ .js .ide-full .ws { flex: 1 1 auto; min-height: 0; }
97
+ .js .ide-full .ws-main { overflow: hidden; }
98
+ .js .ide-full .ws-editor { flex: 1 1 auto; overflow: hidden; }
99
+ .js .ide-full .ws-editor .ce { flex: 1 1 auto; min-height: 0; grid-template-rows: minmax(0, 1fr) auto; border: 0; border-radius: 0; }
100
+ .js .ide-full .ws-editor .ce-main { min-height: 0; }
101
+ .js .ide-full .ws-editor .ce textarea.ce-input { height: 100%; min-height: 0; resize: none; }
102
+ .js .ide-full .ws-output { flex: 0 0 var(--ws-out); }
103
+ .js .ide-full .ws-pane { flex: 1 1 auto; }
104
+ .js .ide-full .ws-nav { overflow: hidden; }
105
+ }
106
+ @media (max-width: 899px) {
107
+ .ws-toolbar .btn span:not(.sr-only) { display: none; }
108
+ .ws-toolbar .btn-hot span:not(.sr-only) { display: inline; }
109
+ }
110
+ /* fullscreen: only the editor and its toolbar */
111
+ body.ws-fullscreen .ide-rail, body.ws-fullscreen .ide-bar, body.ws-fullscreen .ide-status, body.ws-fullscreen .ide-banner, body.ws-fullscreen .ws-page > .sw-tabs,
112
+ body.ws-fullscreen .ws-nav, body.ws-fullscreen .ws-split, body.ws-fullscreen .ws-output, body.ws-fullscreen .ws-runs-as { display: none; }
113
+ body.ws-fullscreen .ws { grid-template-columns: minmax(0, 1fr); }
114
+ body.ide.ws-fullscreen { padding-left: 0; }
115
+
116
+ /* (sprint 92) Query Result: toolbar, sticky header, numbers right-aligned, sortable headings, long values cut */
117
+ .ws-result-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 6px; }
118
+ .ws-result-bar .ws-info { margin: 0; }
119
+ .ws-result-bar .btn { display: inline-flex; align-items: center; gap: 4px; }
120
+ .ws-result-bar .icon { width: 14px; height: 14px; }
121
+ .ws-dl > summary { list-style: none; }
122
+ .ws-dl > summary::-webkit-details-marker { display: none; }
123
+ .ws-dl .menu-links button { display: block; width: 100%; text-align: left; background: none; border: 0; padding: 6px 12px; font: inherit; color: var(--text); cursor: pointer; }
124
+ .ws-dl .menu-links button:hover, .ws-dl .menu-links button:focus-visible { background: var(--surface-3); }
125
+ .ws-dl-form { display: none; }
126
+ .ws-grid thead th { position: sticky; top: 0; z-index: 1; }
127
+ .ws-grid .num { text-align: right; font-variant-numeric: tabular-nums; }
128
+ .ws-grid td { max-width: 32rem; overflow: hidden; text-overflow: ellipsis; }
129
+ .js .ws-grid td:not(.ws-rownum) { cursor: zoom-in; }
130
+ .ws-sort { background: none; border: 0; padding: 0; font: inherit; color: inherit; cursor: pointer; }
131
+ .ws-sort:focus-visible { outline: 2px solid var(--hot); outline-offset: 2px; }
132
+ .js .ws-sort-label { display: none; }
133
+ th[aria-sort=ascending] .ws-sort::after { content: ' ▲'; font-size: 0.75em; }
134
+ th[aria-sort=descending] .ws-sort::after { content: ' ▼'; font-size: 0.75em; }
135
+ .ws-cell { width: min(48rem, calc(100vw - 32px)); max-height: 80vh; padding: 0; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); }
136
+ .ws-cell-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--border); }
137
+ .ws-cell pre { margin: 0; padding: 12px; max-height: 65vh; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12.5px; }
138
+
139
+ /* (sprint 92) plans as a tree */
140
+ .ws-plan-tree { list-style: none; margin: 0; padding-left: 18px; border-left: 1px dashed var(--border); }
141
+ .ws-plan-root { padding-left: 0; border-left: 0; }
142
+ .ws-plan-tree summary { cursor: pointer; padding: 3px 4px; border-radius: 3px; }
143
+ .ws-plan-tree summary:hover { background: var(--surface-3); }
144
+ .ws-plan-op { font-weight: 650; }
145
+ .ws-plan-target { color: var(--gold); }
146
+ .ws-plan-nums { color: var(--muted); font-size: 12px; margin-left: 6px; }
147
+ .ws-plan-tree .tag { margin-left: 6px; font-size: 11px; }
148
+ .ws-plan-tree .is-slowest > summary { background: color-mix(in srgb, var(--warn) 15%, transparent); }
149
+ .ws-plan-details { display: grid; grid-template-columns: max-content 1fr; gap: 2px 10px; margin: 2px 0 4px 22px; font-size: 12px; }
150
+ .ws-plan-details dt { color: var(--muted); }
151
+ .ws-plan-details dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
152
+ .ws-plan-text { margin-top: 10px; }
153
+ .ws-plan-text > summary { cursor: pointer; color: var(--muted); }
154
+
155
+ /* (sprint 92) SQL History */
156
+ .ws-hist-bar { display: flex; gap: 6px; margin-bottom: 6px; }
157
+ .ws-hist-bar input { flex: 1; min-width: 0; }
158
+ .ws-hist-icon .icon { width: 14px; height: 14px; }
159
+ .ws-hist-icon.is-ok { color: #2f7d32; }
160
+ .ws-hist-icon.is-err { color: #c0392b; }
161
+ .ws-history li { align-items: center; }
162
+ .ws-history .btn { flex: none; }
163
+ @media (max-width: 640px) { .ws-hist-meta { display: none; } }
164
+
165
+ /* (sprint 93) worksheet tabs, the file menu, Files, the find bar, the values dialog */
166
+ .ws-wtabs { display: none; align-items: stretch; gap: 0; overflow-x: auto; border-bottom: 1px solid var(--border); background: var(--surface-2); flex: none; }
167
+ .js .ws-wtabs { display: flex; }
168
+ .ws-wtab { display: flex; align-items: center; border-right: 1px solid var(--border); }
169
+ .ws-wtab [role=tab] { border: 0; background: none; color: var(--muted); padding: 5px 4px 5px 10px; font: inherit; font-size: 12.5px; cursor: pointer; white-space: nowrap; max-width: 14rem; overflow: hidden; text-overflow: ellipsis; }
170
+ .ws-wtab [role=tab][aria-selected=true] { color: var(--text); box-shadow: inset 0 -2px 0 var(--hot); }
171
+ .ws-wtab-close, .ws-wtab-new { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 2px 8px; font-size: 14px; line-height: 1; }
172
+ .ws-wtab-close:hover, .ws-wtab-new:hover { color: var(--text); }
173
+ .ws-file > summary { list-style: none; max-width: 14rem; }
174
+ .ws-file > summary::-webkit-details-marker { display: none; }
175
+ .ws-file-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
176
+ .ws-file-panel { display: flex; flex-direction: column; gap: 6px; min-width: 16rem; }
177
+ .ws-file-actions { display: flex; flex-wrap: wrap; gap: 4px; }
178
+ .ws-file-error { margin: 6px 8px; }
179
+ .ws-nav-switch { display: none; border-bottom: 1px solid var(--border); }
180
+ .js .ws-nav-switch { display: flex; }
181
+ .ws-nav-switch [role=tab] { flex: 1; border: 0; background: none; color: var(--muted); padding: 6px; font: inherit; font-size: 12.5px; cursor: pointer; }
182
+ .ws-nav-switch [role=tab][aria-selected=true] { color: var(--text); box-shadow: inset 0 -2px 0 var(--hot); }
183
+ .ws-nav-panel { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
184
+ .ws-nav-panel[hidden] { display: none; }
185
+ .ws-files { padding: 4px 0; font-size: 13px; overflow: auto; }
186
+ .ws-files-title { font-size: 12.5px; margin: 4px 10px; }
187
+ .js .ws-files-title { display: none; }
188
+ .ws-files ul { list-style: none; margin: 0; padding: 0; }
189
+ .ws-files a { display: flex; align-items: center; gap: 6px; padding: 3px 10px; color: var(--text); text-decoration: none; min-width: 0; }
190
+ .ws-files a span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
191
+ .ws-files a:hover, .ws-files a[aria-current] { background: var(--surface-3); }
192
+ .ws-files .icon { width: 14px; height: 14px; color: var(--muted); flex: none; }
193
+ .ws-findbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 8px; border-bottom: 1px solid var(--border); background: var(--surface-2); flex: none; }
194
+ .ws-findbar[hidden] { display: none; }
195
+ .ws-findbar input:not([type=checkbox]) { min-width: 0; width: 12rem; font-size: 13px; }
196
+ .ws-findbar .ws-goto { width: 5rem; }
197
+ .ws-aa { font-weight: 700; font-size: 12px; }
198
+ .ws-keylist { display: grid; grid-template-columns: max-content 1fr; gap: 3px 12px; margin: 0 0 8px; font-size: 12.5px; }
199
+ .ws-keylist dt { font-family: var(--mono, monospace); color: var(--muted); }
200
+ .ws-keylist dd { margin: 0; }
201
+ .ws-kwcase { display: flex; gap: 6px; align-items: center; margin-bottom: 8px; font-size: 12.5px; }
202
+ .ws-binds { width: min(32rem, calc(100vw - 32px)); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); padding: 16px; }
203
+ .ws-binds h2 { font-size: 15px; margin: 0 0 6px; }
204
+ .ws-binds-fields { display: grid; gap: 8px; margin: 10px 0; }
205
+ .ws-binds-fields .field { display: grid; grid-template-columns: 8rem 1fr; align-items: center; gap: 8px; }
206
+
207
+ /* (sprint 94) an object's ⋯ menu in the navigator and the Object Browser's tree; Open in a panel */
208
+ .ws-obj, .ob-item { position: relative; }
209
+ .ws-obj > details.menu.obj-menu, .ob-item > details.menu.obj-menu { position: absolute; right: 4px; top: 1px; z-index: 2; display: block; margin: 0; }
210
+ details.menu.obj-menu > summary { display: block; list-style: none; cursor: pointer; padding: 0 6px; border: 0; background: none; border-radius: 3px; color: var(--muted); line-height: 1.6; opacity: 0; }
211
+ details.menu.obj-menu > summary::before, details.menu.obj-menu > summary::after { content: none; }
212
+ .obj-menu > summary::-webkit-details-marker { display: none; }
213
+ .ws-obj:hover > .obj-menu > summary, .ob-item:hover > .obj-menu > summary, .obj-menu:focus-within > summary, .obj-menu[open] > summary { opacity: 1; }
214
+ .obj-menu > summary:hover { background: var(--surface-3); color: var(--text); }
215
+ @media (hover: none) { .obj-menu > summary { opacity: 1; } }
216
+ .obj-menu .menu-panel { right: 0; left: auto; min-width: 14rem; }
217
+ .ws-obj .tag { margin-right: 22px; }
218
+ .obj-slide { position: fixed; inset: 0 0 0 auto; margin: 0; width: min(46rem, 100vw); max-width: 100vw; height: 100vh; height: 100dvh; max-height: none; padding: 0;
219
+ border: 0; border-left: 1px solid var(--border); background: var(--surface); color: var(--text); overflow: hidden; }
220
+ .obj-slide[open] { display: flex; flex-direction: column; }
221
+ .obj-slide::backdrop { background: rgb(0 0 0 / 35%); }
222
+ .obj-slide-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--border); }
223
+ .obj-slide-head strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
224
+ .obj-slide-body { flex: 1; overflow: auto; padding: 10px 12px; }
225
+ .obj-slide-body[aria-busy=true] { opacity: 0.6; }
226
+
227
+ /* (sprint 95) Table Properties */
228
+ .te-head { max-width: 48rem; }
229
+ .te-tabs { display: none; flex-wrap: wrap; gap: 0; border-bottom: 1px solid var(--border); margin: 12px 0; min-width: 0; }
230
+ .js .te-tabs { display: flex; }
231
+ .te-tabs [role=tab] { border: 0; background: none; color: var(--muted); padding: 7px 12px; font: inherit; cursor: pointer; white-space: nowrap; }
232
+ .te-tabs [role=tab][aria-selected=true] { color: var(--text); box-shadow: inset 0 -2px 0 var(--hot); }
233
+ .te-section { margin-bottom: 16px; }
234
+ .js .te-section > h2 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
235
+ .te-table td { padding: 3px 4px; vertical-align: middle; }
236
+ .te-table input:not([type=checkbox]), .te-table select { width: 100%; min-width: 7rem; font-size: 13px; }
237
+ .te-table .te-c { text-align: center; width: 1%; white-space: nowrap; }
238
+ .te-move { width: 1%; white-space: nowrap; }
239
+ .te-move .btn { padding: 0 5px; }
240
+ .te-drop { display: inline-flex; gap: 4px; align-items: center; font-size: 12px; color: var(--muted); }
241
+ .te-removed td:not(:last-child) { opacity: 0.4; text-decoration: line-through; }
242
+ .te-generated { font-size: 12px; }
243
+ .te-ddl { max-height: 60vh; overflow: auto; }
244
+ .te-buttons { position: sticky; bottom: 0; background: var(--bg); padding: 8px 0; border-top: 1px solid var(--border); }
245
+
246
+ /* (sprint 96) the header's search, the search dialog, the SQL Workshop Launchpad, pins */
247
+ .ide-search { margin-left: auto; min-width: 0; }
248
+ .ide-search input { width: 13rem; max-width: 100%; font-size: 12.5px; padding: 3px 8px; }
249
+ @media (max-width: 700px) { .ide-search input { width: 7rem; } }
250
+ .palette { width: min(40rem, calc(100vw - 32px)); margin-top: 12vh; padding: 0; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text); }
251
+ .palette::backdrop { background: rgb(0 0 0 / 35%); }
252
+ .palette input { width: 100%; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; padding: 12px 14px; font-size: 15px; }
253
+ .palette-list { list-style: none; margin: 0; padding: 4px 0; max-height: 50vh; overflow: auto; }
254
+ .palette-list li { padding: 6px 14px; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
255
+ .palette-list li[aria-selected=true] { background: var(--sel-bg); color: var(--sel-text); }
256
+ .palette-list li[aria-selected=true] .muted { color: inherit; opacity: 0.85; }
257
+ .palette-status { margin: 0; padding: 6px 14px; border-top: 1px solid var(--border); }
258
+ .pal-results { list-style: none; padding: 0; }
259
+ .pal-results a { display: flex; gap: 8px; align-items: center; padding: 6px 0; text-decoration: none; }
260
+ .lp-pins { margin-bottom: 16px; }
261
+ .lp-pins h2, .lp-group h2 { font-size: 14px; margin: 0 0 8px; }
262
+ .lp-chips { list-style: none; margin: 0 0 8px; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
263
+ .lp-chips li { display: flex; align-items: center; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); padding-left: 10px; }
264
+ .lp-chips a { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; color: var(--text); padding: 4px 0; font-size: 13px; }
265
+ .lp-recent li { padding-right: 10px; border-style: dashed; }
266
+ .lp-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--border); margin-bottom: 10px; }
267
+ .lp-tabs [role=tab] { border: 0; background: none; color: var(--muted); padding: 7px 12px; font: inherit; cursor: pointer; }
268
+ .lp-tabs [role=tab][aria-selected=true] { color: var(--text); box-shadow: inset 0 -2px 0 var(--hot); }
269
+ .js .lp-group > h2 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
270
+ .lp-list { list-style: none; margin: 0 0 16px; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 8px; }
271
+ .lp-tool { display: flex; align-items: flex-start; gap: 4px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: 8px; }
272
+ .lp-tool > a { flex: 1; display: flex; gap: 8px; text-decoration: none; color: var(--text); min-width: 0; }
273
+ .lp-tool > a span { display: flex; flex-direction: column; min-width: 0; }
274
+ .lp-tool small { font-size: 12px; }
275
+ .pin-form { display: inline; margin: 0; }
276
+ .pin-btn { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 2px 6px; border-radius: 3px; }
277
+ .pin-btn:hover { color: var(--text); background: var(--surface-3); }
278
+ .pin-btn.is-pinned { color: var(--hot); }
279
+ .pin-btn .icon { width: 14px; height: 14px; }
280
+ .ob-pin { float: right; }
281
+
282
+ /* (sprint 97) charts: the worksheet's Chart tab, the Charts page and its dashboard */
283
+ .ch-controls { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: flex-end; margin-bottom: 8px; font-size: 13px; }
284
+ .ch-controls > label { display: flex; flex-direction: column; gap: 2px; }
285
+ .ch-controls fieldset { display: flex; flex-wrap: wrap; gap: 4px 10px; border: 1px solid var(--border); border-radius: var(--radius); padding: 4px 8px; margin: 0; }
286
+ .ch-controls legend { font-size: 12px; color: var(--muted); padding: 0 4px; }
287
+ .ch-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 8px; }
288
+ .ch-actions input { flex: 0 1 16rem; width: auto; min-width: 0; }
289
+ .ch-view { min-width: 0; max-width: 60rem; }
290
+ .ch-dashboard { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 420px), 1fr)); gap: 12px; margin-bottom: 16px; }
291
+ .ch-card { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: 10px 12px; min-width: 0; }
292
+ .ch-card h2 { font-size: 14px; margin: 0 0 6px; }
293
+ .ch-edit { max-width: 48rem; margin-top: 16px; }