pgkiln 0.45.0 → 0.47.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 (93) hide show
  1. package/db/migrations/106_form_designer.sql +102 -0
  2. package/db/migrations/107_app_version.sql +9 -0
  3. package/db/migrations/108_sql_history.sql +25 -0
  4. package/docs/apex-feature-parity.md +33 -4
  5. package/docs/guide/03-builder.md +123 -28
  6. package/docs/guide/04-pages-and-regions.md +74 -2
  7. package/docs/guide/05-items.md +14 -2
  8. package/docs/guide/08-security.md +1 -1
  9. package/docs/guide/09-reference.md +1 -1
  10. package/docs/guide/10-tutorial.md +1 -1
  11. package/docs/guide/11-from-apex.md +2 -1
  12. package/docs/guide/12-development.md +26 -3
  13. package/docs/guide/16-files.md +2 -2
  14. package/examples/hr/hr_61_form_designer.sql +80 -0
  15. package/package.json +1 -1
  16. package/public/app.css +62 -2
  17. package/public/app.js +244 -35
  18. package/public/builder.css +21 -1
  19. package/public/builder.js +67 -0
  20. package/public/sw.js +2 -1
  21. package/public/worksheet.css +244 -0
  22. package/public/worksheet.js +665 -0
  23. package/src/app.ts +9 -1
  24. package/src/builder/ai-builder.ts +3 -3
  25. package/src/builder/appwizard.ts +22 -6
  26. package/src/builder/arrange.ts +33 -12
  27. package/src/builder/code-editor.ts +1 -1
  28. package/src/builder/components.ts +1 -1
  29. package/src/builder/designer.ts +44 -17
  30. package/src/builder/errors.ts +65 -0
  31. package/src/builder/form-designer.ts +229 -0
  32. package/src/builder/locks.ts +6 -0
  33. package/src/builder/newapp.ts +2 -1
  34. package/src/builder/object-actions.ts +166 -0
  35. package/src/builder/object-browser.ts +111 -33
  36. package/src/builder/querybuilder.ts +1 -1
  37. package/src/builder/region-help.ts +2 -0
  38. package/src/builder/region-settings.ts +2 -0
  39. package/src/builder/reporter.ts +1 -1
  40. package/src/builder/routes.ts +7 -5
  41. package/src/builder/sampledata.ts +2 -2
  42. package/src/builder/scripts.ts +3 -3
  43. package/src/builder/search.ts +3 -3
  44. package/src/builder/shared.ts +2 -2
  45. package/src/builder/sql.ts +15 -73
  46. package/src/builder/table-editor.ts +219 -0
  47. package/src/builder/ui.ts +5 -3
  48. package/src/builder/unload.ts +8 -4
  49. package/src/builder/worksheet.ts +569 -0
  50. package/src/builder/workspaces.ts +1 -1
  51. package/src/cli/replace.ts +10 -0
  52. package/src/i18n/ar.ts +62 -0
  53. package/src/i18n/cs.ts +62 -0
  54. package/src/i18n/da.ts +62 -0
  55. package/src/i18n/de.ts +62 -0
  56. package/src/i18n/el.ts +62 -0
  57. package/src/i18n/es.ts +62 -0
  58. package/src/i18n/fi.ts +62 -0
  59. package/src/i18n/fr.ts +62 -0
  60. package/src/i18n/he.ts +62 -0
  61. package/src/i18n/it.ts +62 -0
  62. package/src/i18n/ja.ts +62 -0
  63. package/src/i18n/ko.ts +62 -0
  64. package/src/i18n/nb.ts +62 -0
  65. package/src/i18n/pl.ts +62 -0
  66. package/src/i18n/pt.ts +62 -0
  67. package/src/i18n/ru.ts +62 -0
  68. package/src/i18n/sv.ts +62 -0
  69. package/src/i18n/tr.ts +62 -0
  70. package/src/i18n/uk.ts +62 -0
  71. package/src/i18n/zh.ts +62 -0
  72. package/src/i18n.ts +124 -0
  73. package/src/metadata.ts +4 -2
  74. package/src/runtime/devbar.ts +1 -1
  75. package/src/runtime/facet-state.ts +10 -5
  76. package/src/runtime/facets.ts +29 -5
  77. package/src/runtime/form-designer.ts +676 -0
  78. package/src/runtime/grid.ts +15 -8
  79. package/src/runtime/items.ts +33 -21
  80. package/src/runtime/lists.ts +1 -1
  81. package/src/runtime/pdf.ts +2 -1
  82. package/src/runtime/regions.ts +4 -0
  83. package/src/runtime/render.ts +7 -3
  84. package/src/runtime/report.ts +2 -2
  85. package/src/runtime/routes.ts +3 -1
  86. package/src/runtime/smart-filters.ts +2 -1
  87. package/src/runtime/tasks.ts +6 -3
  88. package/src/sqlformat.ts +208 -0
  89. package/src/sqlplan.ts +133 -0
  90. package/src/sqlscript.ts +6 -2
  91. package/src/sqlworksheet.ts +278 -0
  92. package/src/tabledef.ts +226 -0
  93. package/src/unload.ts +14 -3
@@ -0,0 +1,244 @@
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); }